python-pycurl/0000-pycurl-7.19.7-8d654296.patch
2013-08-08 13:18:35 +02:00

12437 lines
376 KiB
Diff

From 9a426178c83f756ec248e9fd0010030ae7c62f2a Mon Sep 17 00:00:00 2001
From: Kjetil Jacobsen <kjetilja@gmail.com>
Date: Tue, 9 Sep 2008 18:49:59 +0000
Subject: [PATCH 001/149] whitespace
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
ChangeLog | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 0fb7f8c..3d68424 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,7 +1,7 @@
Version 7.19.0 [requires libcurl-7.19.0 or better]
--------------
- * Added CURLFILE, ADDRESS_SCOPE and ISSUERCERT options,
+ * Added CURLFILE, ADDRESS_SCOPE and ISSUERCERT options,
as well as the APPCONNECT_TIME info.
* Added PRIMARY_IP info (patch by
--
1.7.1
From b5f6dd9fd839e54db567d0451483a400edad60a4 Mon Sep 17 00:00:00 2001
From: Kjetil Jacobsen <kjetilja@gmail.com>
Date: Mon, 29 Sep 2008 10:56:57 +0000
Subject: [PATCH 002/149] No longer keep copies of string options since this is managed by libcurl
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
ChangeLog | 10 +++++++++-
src/pycurl.c | 58 +---------------------------------------------------------
2 files changed, 10 insertions(+), 58 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 3d68424..618654d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,12 @@
-Version 7.19.0 [requires libcurl-7.19.0 or better]
+Version 7.19.1 [requires libcurl-7.19.0 or better]
+--------------
+
+ * No longer keep string options copies in the
+ Curl Python objects, since string options are
+ now managed by libcurl.
+
+
+Version 7.19.0
--------------
* Added CURLFILE, ADDRESS_SCOPE and ISSUERCERT options,
diff --git a/src/pycurl.c b/src/pycurl.c
index 50ec9ce..189f5d6 100644
--- a/src/pycurl.c
+++ b/src/pycurl.c
@@ -97,12 +97,6 @@ static void pycurl_ssl_cleanup(void);
/* Calculate the number of OBJECTPOINT options we need to store */
#define OPTIONS_SIZE ((int)CURLOPT_LASTENTRY % 10000)
#define MOPTIONS_SIZE ((int)CURLMOPT_LASTENTRY % 10000)
-static int OPT_INDEX(int o)
-{
- assert(o >= CURLOPTTYPE_OBJECTPOINT);
- assert(o < CURLOPTTYPE_OBJECTPOINT + OPTIONS_SIZE);
- return o - CURLOPTTYPE_OBJECTPOINT;
-}
/* Type objects */
static PyObject *ErrorObject = NULL;
@@ -161,7 +155,6 @@ typedef struct {
PyObject *writedata_fp;
PyObject *writeheader_fp;
/* misc */
- void *options[OPTIONS_SIZE]; /* for OBJECTPOINT options */
char error[CURL_ERROR_SIZE+1];
} CurlObject;
@@ -741,7 +734,6 @@ util_curl_new(void)
self->writeheader_fp = NULL;
/* Zero string pointer memory buffer used by setopt */
- memset(self->options, 0, sizeof(self->options));
memset(self->error, 0, sizeof(self->error));
return self;
@@ -804,7 +796,6 @@ do_curl_new(PyObject *dummy)
free(s);
goto error;
}
- self->options[ OPT_INDEX(CURLOPT_USERAGENT) ] = s; s = NULL;
/* Success - return new object */
return self;
@@ -872,7 +863,6 @@ static void
util_curl_close(CurlObject *self)
{
CURL *handle;
- int i;
/* Zero handle and thread-state to disallow any operations to be run
* from now on */
@@ -916,16 +906,6 @@ util_curl_close(CurlObject *self)
SFREE(self->postquote);
SFREE(self->prequote);
#undef SFREE
-
- /* Last, free the options. This must be done after the curl handle
- * is closed since libcurl assumes that some options are valid when
- * invoking curl_easy_cleanup(). */
- for (i = 0; i < OPTIONS_SIZE; i++) {
- if (self->options[i] != NULL) {
- free(self->options[i]);
- self->options[i] = NULL;
- }
- }
}
@@ -1424,8 +1404,6 @@ verbose_error:
static PyObject*
do_curl_reset(CurlObject *self)
{
- unsigned int i;
-
curl_easy_reset(self->handle);
/* Decref callbacks and file handles */
@@ -1443,15 +1421,6 @@ do_curl_reset(CurlObject *self)
SFREE(self->postquote);
SFREE(self->prequote);
#undef SFREE
-
- /* Last, free the options */
- for (i = 0; i < OPTIONS_SIZE; i++) {
- if (self->options[i] != NULL) {
- free(self->options[i]);
- self->options[i] = NULL;
- }
- }
-
return Py_None;
}
@@ -1461,7 +1430,6 @@ static PyObject *
util_curl_unsetopt(CurlObject *self, int option)
{
int res;
- int opt_index = -1;
#define SETOPT2(o,x) \
if ((res = curl_easy_setopt(self->handle, (o), (x))) != CURLE_OK) goto error
@@ -1502,7 +1470,6 @@ util_curl_unsetopt(CurlObject *self, int option)
case CURLOPT_SSL_CIPHER_LIST:
case CURLOPT_USERPWD:
SETOPT((char *) 0);
- opt_index = OPT_INDEX(option);
break;
/* info: we explicitly list unsupported options here */
@@ -1512,11 +1479,6 @@ util_curl_unsetopt(CurlObject *self, int option)
return NULL;
}
- if (opt_index >= 0 && self->options[opt_index] != NULL) {
- free(self->options[opt_index]);
- self->options[opt_index] = NULL;
- }
-
Py_INCREF(Py_None);
return Py_None;
@@ -1587,8 +1549,6 @@ do_curl_setopt(CurlObject *self, PyObject *args)
if (PyString_Check(obj)) {
char *str = NULL;
Py_ssize_t len = -1;
- char *buf;
- int opt_index;
/* Check that the option specified a string as well as the input */
switch (option) {
@@ -1651,28 +1611,12 @@ do_curl_setopt(CurlObject *self, PyObject *args)
}
/* Allocate memory to hold the string */
assert(str != NULL);
- if (len <= 0)
- buf = strdup(str);
- else {
- buf = (char *) malloc(len);
- if (buf) memcpy(buf, str, len);
- }
- if (buf == NULL)
- return PyErr_NoMemory();
/* Call setopt */
- res = curl_easy_setopt(self->handle, (CURLoption)option, buf);
+ res = curl_easy_setopt(self->handle, (CURLoption)option, str);
/* Check for errors */
if (res != CURLE_OK) {
- free(buf);
CURLERROR_RETVAL();
}
- /* Save allocated option buffer */
- opt_index = OPT_INDEX(option);
- if (self->options[opt_index] != NULL) {
- free(self->options[opt_index]);
- self->options[opt_index] = NULL;
- }
- self->options[opt_index] = buf;
Py_INCREF(Py_None);
return Py_None;
}
--
1.7.1
From f241a60acc67cc8cbf3d36a2f3d55e1abf88bc33 Mon Sep 17 00:00:00 2001
From: Christopher Warner <cwarner@kernelcode.com>
Date: Fri, 23 Apr 2010 16:06:41 +0000
Subject: [PATCH 003/149] Fixes https://sourceforge.net/tracker/?func=detail&aid=2812016&group_id=28236&atid=392777 with applied patch from sourceforge user dbprice1.
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
setup.py | 21 +++++++++++++++++----
1 files changed, 17 insertions(+), 4 deletions(-)
diff --git a/setup.py b/setup.py
index 1c3831b..632399d 100644
--- a/setup.py
+++ b/setup.py
@@ -9,7 +9,7 @@ PACKAGE = "pycurl"
PY_PACKAGE = "curl"
VERSION = "7.19.0"
-import glob, os, re, sys, string
+import glob, os, re, sys, string, subprocess
import distutils
from distutils.core import setup
from distutils.extension import Extension
@@ -96,9 +96,22 @@ else:
include_dirs.append(e[2:])
else:
extra_compile_args.append(e)
- libs = split_quoted(
- os.popen("'%s' --libs" % CURL_CONFIG).read()+\
- os.popen("'%s' --static-libs" % CURL_CONFIG).read())
+
+ # Run curl-config --libs and --static-libs. Some platforms may not
+ # support one or the other of these curl-config options, so gracefully
+ # tolerate failure of either, but not both.
+ optbuf = ""
+ for option in ["--libs", "--static-libs"]:
+ p = subprocess.Popen("'%s' %s" % (CURL_CONFIG, option), shell=True,
+ stdout=subprocess.PIPE)
+ (stdout, stderr) = p.communicate()
+ if p.wait() == 0:
+ optbuf += stdout
+ if optbuf == "":
+ raise Exception, ("Neither of curl-config --libs or --static-libs" +
+ "produced output")
+ libs = split_quoted(optbuf)
+
for e in libs:
if e[:2] == "-l":
libraries.append(e[2:])
--
1.7.1
From 70114226c76ee06c6bacb488734796551ac9b2f9 Mon Sep 17 00:00:00 2001
From: Christopher Warner <cwarner@kernelcode.com>
Date: Wed, 28 Apr 2010 16:02:41 +0000
Subject: [PATCH 004/149] Fixes refcount bug and provides better organization of PyCurl object. Submitted by dbprice1.
https://sourceforge.net/tracker/?func=detail&aid=2893665&group_id=28236&atid=392777
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
src/pycurl.c | 87 ++++++++++++++++++++++++++++++++++++++--------------------
1 files changed, 57 insertions(+), 30 deletions(-)
diff --git a/src/pycurl.c b/src/pycurl.c
index 189f5d6..47de850 100644
--- a/src/pycurl.c
+++ b/src/pycurl.c
@@ -739,64 +739,80 @@ util_curl_new(void)
return self;
}
-
-/* constructor - this is a module-level function returning a new instance */
-static CurlObject *
-do_curl_new(PyObject *dummy)
+/* initializer - used to intialize curl easy handles for use with pycurl */
+static int
+util_curl_init(CurlObject *self)
{
- CurlObject *self = NULL;
int res;
char *s = NULL;
- UNUSED(dummy);
-
- /* Allocate python curl object */
- self = util_curl_new();
- if (self == NULL)
- return NULL;
-
- /* Initialize curl handle */
- self->handle = curl_easy_init();
- if (self->handle == NULL)
- goto error;
-
/* Set curl error buffer and zero it */
res = curl_easy_setopt(self->handle, CURLOPT_ERRORBUFFER, self->error);
- if (res != CURLE_OK)
- goto error;
+ if (res != CURLE_OK) {
+ return (-1);
+ }
memset(self->error, 0, sizeof(self->error));
/* Set backreference */
res = curl_easy_setopt(self->handle, CURLOPT_PRIVATE, (char *) self);
- if (res != CURLE_OK)
- goto error;
+ if (res != CURLE_OK) {
+ return (-1);
+ }
/* Enable NOPROGRESS by default, i.e. no progress output */
res = curl_easy_setopt(self->handle, CURLOPT_NOPROGRESS, (long)1);
- if (res != CURLE_OK)
- goto error;
+ if (res != CURLE_OK) {
+ return (-1);
+ }
/* Disable VERBOSE by default, i.e. no verbose output */
res = curl_easy_setopt(self->handle, CURLOPT_VERBOSE, (long)0);
- if (res != CURLE_OK)
- goto error;
+ if (res != CURLE_OK) {
+ return (-1);
+ }
/* Set FTP_ACCOUNT to NULL by default */
res = curl_easy_setopt(self->handle, CURLOPT_FTP_ACCOUNT, NULL);
- if (res != CURLE_OK)
- goto error;
+ if (res != CURLE_OK) {
+ return (-1);
+ }
/* Set default USERAGENT */
s = (char *) malloc(7 + strlen(LIBCURL_VERSION) + 1);
- if (s == NULL)
- goto error;
+ if (s == NULL) {
+ return (-1);
+ }
strcpy(s, "PycURL/"); strcpy(s+7, LIBCURL_VERSION);
res = curl_easy_setopt(self->handle, CURLOPT_USERAGENT, (char *) s);
if (res != CURLE_OK) {
free(s);
- goto error;
+ return (-1);
}
+ return (0);
+}
+
+/* constructor - this is a module-level function returning a new instance */
+static CurlObject *
+do_curl_new(PyObject *dummy)
+{
+ CurlObject *self = NULL;
+ int res;
+
+ UNUSED(dummy);
+
+ /* Allocate python curl object */
+ self = util_curl_new();
+ if (self == NULL)
+ return NULL;
+
+ /* Initialize curl handle */
+ self->handle = curl_easy_init();
+ if (self->handle == NULL)
+ goto error;
+ res = util_curl_init(self);
+ if (res < 0)
+ goto error;
/* Success - return new object */
return self;
@@ -1404,6 +1420,8 @@ verbose_error:
static PyObject*
do_curl_reset(CurlObject *self)
{
+ int res;
+
curl_easy_reset(self->handle);
/* Decref callbacks and file handles */
@@ -1421,10 +1439,19 @@ do_curl_reset(CurlObject *self)
SFREE(self->postquote);
SFREE(self->prequote);
#undef SFREE
+ res = util_curl_init(self);
+ if (res < 0) {
+ Py_DECREF(self); /* this also closes self->handle */
+ PyErr_SetString(ErrorObject, "resetting curl failed");
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
return Py_None;
}
/* --------------- unsetopt/setopt/getinfo --------------- */
+ int res;
static PyObject *
util_curl_unsetopt(CurlObject *self, int option)
--
1.7.1
From a607b0c9f4676858fcf6335e9fb0c9ed6a9c3069 Mon Sep 17 00:00:00 2001
From: Christopher Warner <cwarner@kernelcode.com>
Date: Wed, 28 Apr 2010 16:03:40 +0000
Subject: [PATCH 005/149] Test for reset fixes refcount bug
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/test_reset.py | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 75 insertions(+), 0 deletions(-)
create mode 100644 tests/test_reset.py
diff --git a/tests/test_reset.py b/tests/test_reset.py
new file mode 100644
index 0000000..1addcfe
--- /dev/null
+++ b/tests/test_reset.py
@@ -0,0 +1,75 @@
+#!/usr/bin/python
+
+import sys
+import pycurl
+
+saw_error = 1
+
+def main():
+ global saw_error
+
+ pycurl.global_init(pycurl.GLOBAL_DEFAULT)
+
+ outf = file("/dev/null", "rb+")
+ cm = pycurl.CurlMulti()
+
+ # Set multi handle's options
+ cm.setopt(pycurl.M_PIPELINING, 1)
+
+ eh = pycurl.Curl()
+
+ for x in range(1, 20):
+
+ eh.setopt(pycurl.WRITEDATA, outf)
+ eh.setopt(pycurl.URL, sys.argv[1])
+ cm.add_handle(eh)
+
+ while 1:
+ ret, active_handles = cm.perform()
+ if ret != pycurl.E_CALL_MULTI_PERFORM:
+ break
+
+ while active_handles:
+ ret = cm.select(1.0)
+ if ret == -1:
+ continue
+ while 1:
+ ret, active_handles = cm.perform()
+ if ret != pycurl.E_CALL_MULTI_PERFORM:
+ break
+
+ count, good, bad = cm.info_read()
+
+ for h, en, em in bad:
+ print "Transfer to %s failed with %d, %s\n" % \
+ (h.getinfo(pycurl.EFFECTIVE_URL), en, em)
+ raise RuntimeError
+
+ for h in good:
+ httpcode = h.getinfo(pycurl.RESPONSE_CODE)
+ if httpcode != 200:
+ print "Transfer to %s failed with code %d\n" %\
+ (h.getinfo(pycurl.EFFECTIVE_URL), httpcode)
+ raise RuntimeError
+
+ else:
+ print "Recd %d bytes from %s" % \
+ (h.getinfo(pycurl.SIZE_DOWNLOAD),
+ h.getinfo(pycurl.EFFECTIVE_URL))
+
+ cm.remove_handle(eh)
+ eh.reset()
+
+ eh.close()
+ cm.close()
+ outf.close()
+
+ pycurl.global_cleanup()
+
+
+if __name__ == '__main__':
+ if len(sys.argv) != 2:
+ print "Usage: %s <url>" % sys.argv[0]
+ sys.exit(2)
+ main()
+
--
1.7.1
From f773340ec40d5988d4a5b75e64b898dbe4a89800 Mon Sep 17 00:00:00 2001
From: Christopher Warner <cwarner@kernelcode.com>
Date: Wed, 28 Apr 2010 16:06:35 +0000
Subject: [PATCH 006/149] Added myself to the list of maintainers
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
setup.py | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/setup.py b/setup.py
index 632399d..33704ef 100644
--- a/setup.py
+++ b/setup.py
@@ -197,8 +197,8 @@ setup_args = get_kw(
description="PycURL -- cURL library module for Python",
author="Kjetil Jacobsen, Markus F.X.J. Oberhumer",
author_email="kjetilja at gmail.com, markus at oberhumer.com",
- maintainer="Kjetil Jacobsen, Markus F.X.J. Oberhumer",
- maintainer_email="kjetilja at gmail.com, markus at oberhumer.com",
+ maintainer="Kjetil Jacobsen, Markus F.X.J. Oberhumer, Christopher Warner",
+ maintainer_email="kjetilja at gmail.com, markus at oberhumer.com, cwarner at kernelcode.com",
url="http://pycurl.sourceforge.net/",
license="LGPL/MIT",
data_files=get_data_files(),
--
1.7.1
From 67e0b9ac7c521b39d7dc779a79d21009f5619fa1 Mon Sep 17 00:00:00 2001
From: Christopher Warner <cwarner@kernelcode.com>
Date: Tue, 4 May 2010 18:47:08 +0000
Subject: [PATCH 007/149] Updating ChangeLog with relevant changes
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
ChangeLog | 11 +++++++++++
1 files changed, 11 insertions(+), 0 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 618654d..885c8b0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+Version 7.19.2
+--------------
+
+ * Cleaned up website
+
+ * Fix pycurl.reset() (patch by <johansen at sun.com>).
+
+ * Fix install routine in setup.py where
+ certain platforms (Solaris, Mac OSX, etc)
+ would search for a static copy of libcurl (dbp)
+
Version 7.19.1 [requires libcurl-7.19.0 or better]
--------------
--
1.7.1
From 310638226dbe77b71a8eb9f8da58cf15d236337a Mon Sep 17 00:00:00 2001
From: Christopher Warner <cwarner@kernelcode.com>
Date: Wed, 13 Oct 2010 15:53:40 +0000
Subject: [PATCH 008/149] Added CURLOPT_SEEKFUNCTION, CURLOPT_SEEKDATA
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
src/pycurl.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 88 insertions(+), 1 deletions(-)
diff --git a/src/pycurl.c b/src/pycurl.c
index 47de850..ac448b0 100644
--- a/src/pycurl.c
+++ b/src/pycurl.c
@@ -150,6 +150,7 @@ typedef struct {
PyObject *debug_cb;
PyObject *ioctl_cb;
PyObject *opensocket_cb;
+ PyObject *seek_cb;
/* file objects */
PyObject *readdata_fp;
PyObject *writedata_fp;
@@ -727,6 +728,7 @@ util_curl_new(void)
self->debug_cb = NULL;
self->ioctl_cb = NULL;
self->opensocket_cb = NULL;
+ self->seek_cb = NULL;
/* Set file object pointers to NULL by default */
self->readdata_fp = NULL;
@@ -1181,6 +1183,82 @@ verbose_error:
goto silent_error;
}
+static int
+seek_callback(void *stream, curl_off_t offset, int origin)
+{
+ CurlObject *self;
+ PyThreadState *tmp_state;
+ PyObject *arglist;
+ PyObject *result = NULL;
+ int ret = 2; /* assume error 2 (can't seek, libcurl free to work around). */
+ PyObject *cb;
+ int source = 0; /* assume beginning */
+
+ /* acquire thread */
+ self = (CurlObject *)stream;
+ tmp_state = get_thread_state(self);
+ if (tmp_state == NULL)
+ return ret;
+ PyEval_AcquireThread(tmp_state);
+
+ /* check arguments */
+ switch (origin)
+ {
+ case SEEK_SET:
+ source = 0;
+ break;
+ case SEEK_CUR:
+ source = 1;
+ break;
+ case SEEK_END:
+ source = 2;
+ break;
+ default:
+ source = origin;
+ break;
+ }
+
+ /* run callback */
+ cb = self->seek_cb;
+ if (cb == NULL)
+ goto silent_error;
+ arglist = Py_BuildValue("(i,i)", offset, source);
+ if (arglist == NULL)
+ goto verbose_error;
+ result = PyEval_CallObject(cb, arglist);
+ Py_DECREF(arglist);
+ if (result == NULL)
+ goto verbose_error;
+
+ /* handle result */
+ if (result == Py_None) {
+ ret = 0; /* None means success */
+ }
+ else if (PyInt_Check(result)) {
+ int ret_code = PyInt_AsLong(result);
+ if (ret_code < 0 || ret_code > 2) {
+ PyErr_Format(ErrorObject, "invalid return value for seek callback %d not in (0, 1, 2)", ret_code);
+ goto verbose_error;
+ }
+ ret = ret_code; /* pass the return code from the callback */
+ }
+ else {
+ PyErr_SetString(ErrorObject, "seek callback must return 0 (CURL_SEEKFUNC_OK), 1 (CURL_SEEKFUNC_FAIL), 2 (CURL_SEEKFUNC_CANTSEEK) or None");
+ goto verbose_error;
+ }
+
+silent_error:
+ Py_XDECREF(result);
+ PyEval_ReleaseThread(tmp_state);
+ return ret;
+verbose_error:
+ PyErr_Print();
+ goto silent_error;
+}
+
+
+
+
static size_t
read_callback(char *ptr, size_t size, size_t nmemb, void *stream)
{
@@ -1988,7 +2066,8 @@ do_curl_setopt(CurlObject *self, PyObject *args)
const curl_progress_callback pro_cb = progress_callback;
const curl_debug_callback debug_cb = debug_callback;
const curl_ioctl_callback ioctl_cb = ioctl_callback;
- const curl_opensocket_callback opensocket_cb = opensocket_callback;
+ const curl_opensocket_callback opensocket_cb = opensocket_callback;
+ const curl_seek_callback seek_cb = seek_callback;
switch(option) {
case CURLOPT_WRITEFUNCTION:
@@ -2046,6 +2125,13 @@ do_curl_setopt(CurlObject *self, PyObject *args)
curl_easy_setopt(self->handle, CURLOPT_OPENSOCKETFUNCTION, opensocket_cb);
curl_easy_setopt(self->handle, CURLOPT_OPENSOCKETDATA, self);
break;
+ case CURLOPT_SEEKFUNCTION:
+ Py_INCREF(obj);
+ ZAP(self->seek_cb);
+ self->seek_cb = obj;
+ curl_easy_setopt(self->handle, CURLOPT_SEEKFUNCTION, seek_cb);
+ curl_easy_setopt(self->handle, CURLOPT_SEEKDATA, self);
+ break;
default:
/* None of the function options were recognized, throw exception */
@@ -3616,6 +3702,7 @@ initpycurl(void)
insint_c(d, "PREQUOTE", CURLOPT_PREQUOTE);
insint_c(d, "WRITEHEADER", CURLOPT_WRITEHEADER);
insint_c(d, "HEADERFUNCTION", CURLOPT_HEADERFUNCTION);
+ insint_c(d, "SEEKFUNCTION", CURLOPT_SEEKFUNCTION);
insint_c(d, "COOKIEFILE", CURLOPT_COOKIEFILE);
insint_c(d, "SSLVERSION", CURLOPT_SSLVERSION);
insint_c(d, "TIMECONDITION", CURLOPT_TIMECONDITION);
--
1.7.1
From bd8a7fa3279613f41c503f5946d2576d4df70865 Mon Sep 17 00:00:00 2001
From: Kamil Dudka <kdudka@redhat.com>
Date: Tue, 26 Feb 2013 09:13:24 +0100
Subject: [PATCH 009/149] pycurl.c: remove a left-over global variable introduced in r150
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
src/pycurl.c | 1 -
1 files changed, 0 insertions(+), 1 deletions(-)
diff --git a/src/pycurl.c b/src/pycurl.c
index ac448b0..094bc60 100644
--- a/src/pycurl.c
+++ b/src/pycurl.c
@@ -1529,7 +1529,6 @@ do_curl_reset(CurlObject *self)
}
/* --------------- unsetopt/setopt/getinfo --------------- */
- int res;
static PyObject *
util_curl_unsetopt(CurlObject *self, int option)
--
1.7.1
From de45f3fc04323c619a879e835f8f212c8c887364 Mon Sep 17 00:00:00 2001
From: Kamil Dudka <kdudka@redhat.com>
Date: Mon, 25 Feb 2013 19:50:18 +0100
Subject: [PATCH 010/149] test_internals.py: add a test for ref-counting in reset()
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/test_internals.py | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/tests/test_internals.py b/tests/test_internals.py
index a1a6533..3f5eefd 100644
--- a/tests/test_internals.py
+++ b/tests/test_internals.py
@@ -245,6 +245,11 @@ if 1 and gc:
if opts.verbose >= 1:
print "Tracked objects:", len(gc.get_objects())
+if 1:
+ # Ensure that the refcounting error in "reset" is fixed:
+ for i in xrange(100000):
+ c = Curl()
+ c.reset()
# /***********************************************************************
# // done
--
1.7.1
From be80a76e848ea77764da165d517ae0a8aca64604 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 01:15:00 -0500
Subject: [PATCH 011/149] Add gitignore
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
.gitignore | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 .gitignore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/build
--
1.7.1
From c0bee23c728e61874fc2f89d800685c32b331f80 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Fri, 1 Mar 2013 00:11:47 -0500
Subject: [PATCH 012/149] A much simpler request test, in unittest format
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/request_test.py | 31 +++++++++++++++++++++++++++++++
1 files changed, 31 insertions(+), 0 deletions(-)
create mode 100644 tests/request_test.py
diff --git a/tests/request_test.py b/tests/request_test.py
new file mode 100644
index 0000000..0761d6c
--- /dev/null
+++ b/tests/request_test.py
@@ -0,0 +1,31 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+import pycurl
+import unittest
+try:
+ from cStringIO import StringIO
+except ImportError:
+ try:
+ from StringIO import StringIO
+ except ImportError:
+ from io import StringIO
+
+class RequestTest(unittest.TestCase):
+ def setUp(self):
+ self.curl = pycurl.Curl()
+
+ def tearDown(self):
+ self.curl.close()
+
+ def test_perform_get(self):
+ self.curl.setopt(pycurl.URL, 'http://localhost')
+ self.curl.perform()
+
+ def test_perform_get_with_write_function(self):
+ self.curl.setopt(pycurl.URL, 'http://localhost')
+ sio = StringIO()
+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
+ self.curl.perform()
+ print(sio.getvalue())
--
1.7.1
From c178c8f0b6ca9a839cd344b4ebe9e561d804d297 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Fri, 1 Mar 2013 13:35:59 -0500
Subject: [PATCH 013/149] init.py for test package
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
0 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 tests/__init__.py
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..e69de29
--
1.7.1
From a2a23e3e89e9d4cf7e61bf0259534fb0117ddf09 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Fri, 1 Mar 2013 13:36:08 -0500
Subject: [PATCH 014/149] wsgi application runner
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/runwsgi.py | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 82 insertions(+), 0 deletions(-)
create mode 100644 tests/runwsgi.py
diff --git a/tests/runwsgi.py b/tests/runwsgi.py
new file mode 100644
index 0000000..d658614
--- /dev/null
+++ b/tests/runwsgi.py
@@ -0,0 +1,82 @@
+# Run a WSGI application in a daemon thread
+
+import bottle
+import threading
+import socket
+import time as _time
+
+class Server(bottle.WSGIRefServer):
+ def run(self, handler): # pragma: no cover
+ from wsgiref.simple_server import make_server, WSGIRequestHandler
+ if self.quiet:
+ base = self.options.get('handler_class', WSGIRequestHandler)
+ class QuietHandler(base):
+ def log_request(*args, **kw): pass
+ self.options['handler_class'] = QuietHandler
+ self.srv = make_server(self.host, self.port, handler, **self.options)
+ self.srv.serve_forever(poll_interval=0.1)
+
+def start_bottle_server(app, port, **kwargs):
+ server_thread = ServerThread(app, port, kwargs)
+ server_thread.daemon = True
+ server_thread.start()
+
+ ok = False
+ for i in range(10):
+ try:
+ conn = socket.create_connection(('127.0.0.1', port), 0.1)
+ except socket.error as e:
+ _time.sleep(0.1)
+ else:
+ conn.close()
+ ok = True
+ break
+ if not ok:
+ import warnings
+ warnings.warn('Server did not start after 1 second')
+
+ return server_thread.server
+
+class ServerThread(threading.Thread):
+ def __init__(self, app, port, server_kwargs):
+ threading.Thread.__init__(self)
+ self.app = app
+ self.port = port
+ self.server_kwargs = server_kwargs
+ self.server = Server(host='localhost', port=self.port, **self.server_kwargs)
+
+ def run(self):
+ bottle.run(self.app, server=self.server, quiet=True)
+
+def app_runner_setup(*specs):
+ '''Returns setup and teardown methods for running a list of WSGI
+ applications in a daemon thread.
+
+ Each argument is an (app, port) pair.
+
+ Return value is a (setup, teardown) function pair.
+
+ The setup and teardown functions expect to be called with an argument
+ on which server state will be stored.
+
+ Example usage with nose:
+
+ >>> setup_module, teardown_module = \
+ runwsgi.app_runner_setup((app_module.app, 8050))
+ '''
+
+ def setup(self):
+ self.servers = []
+ for spec in specs:
+ if len(spec) == 2:
+ app, port = spec
+ kwargs = {}
+ else:
+ app, port, kwargs = spec
+ self.servers.append(start_bottle_server(app, port, **kwargs))
+
+ def teardown(self):
+ for server in self.servers:
+ server.srv.shutdown()
+
+ return [setup, teardown]
--
1.7.1
From 72ac92b738b9709e84ebd56fd055286c173e5fee Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Fri, 1 Mar 2013 14:06:42 -0500
Subject: [PATCH 015/149] Test application to be on the server side
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/app.py | 7 +++++++
1 files changed, 7 insertions(+), 0 deletions(-)
create mode 100644 tests/app.py
diff --git a/tests/app.py b/tests/app.py
new file mode 100644
index 0000000..b173fd6
--- /dev/null
+++ b/tests/app.py
@@ -0,0 +1,7 @@
+import bottle
+
+app = bottle.Bottle()
+
+@app.route('/success')
+def ok():
+ return 'success'
--
1.7.1
From 83482f1c46d57074fa985def4f0aa62cc0df15d6 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Fri, 1 Mar 2013 14:07:04 -0500
Subject: [PATCH 016/149] Use the test application in request test
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/request_test.py | 59 ++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 56 insertions(+), 3 deletions(-)
diff --git a/tests/request_test.py b/tests/request_test.py
index 0761d6c..127fbeb 100644
--- a/tests/request_test.py
+++ b/tests/request_test.py
@@ -2,8 +2,12 @@
# -*- coding: iso-8859-1 -*-
# vi:ts=4:et
+import os
+import sys
+import tempfile
import pycurl
import unittest
+import io
try:
from cStringIO import StringIO
except ImportError:
@@ -12,6 +16,11 @@ except ImportError:
except ImportError:
from io import StringIO
+from . import app
+from . import runwsgi
+
+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380))
+
class RequestTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
@@ -20,12 +29,56 @@ class RequestTest(unittest.TestCase):
self.curl.close()
def test_perform_get(self):
- self.curl.setopt(pycurl.URL, 'http://localhost')
+ # This test performs a GET request without doing anything else.
+ # Unfortunately, the default curl behavior is to print response
+ # body to standard output, which spams test output.
+ # As a result this test is commented out. Uncomment for debugging.
+ # test_perform_get_with_default_write_function is the test
+ # which exercises default curl write handler.
+ return
+
+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
self.curl.perform()
def test_perform_get_with_write_function(self):
- self.curl.setopt(pycurl.URL, 'http://localhost')
+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
sio = StringIO()
self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
self.curl.perform()
- print(sio.getvalue())
+ self.assertEqual('success', sio.getvalue())
+
+ def test_perform_get_with_default_write_function(self):
+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
+ #with tempfile.NamedTemporaryFile() as f:
+ with open('w', 'w+') as f:
+ # nose output capture plugin replaces sys.stdout with a StringIO
+ # instance. We want to redirect the underlying file descriptor
+ # anyway because underlying C code uses it.
+ # But keep track of whether we replace sys.stdout.
+ perform_dup = False
+ if hasattr(sys.stdout, 'fileno'):
+ try:
+ sys.stdout.fileno()
+ perform_dup = True
+ except io.UnsupportedOperation:
+ # stdout is a StringIO
+ pass
+ if perform_dup:
+ saved_stdout_fd = os.dup(sys.stdout.fileno())
+ os.dup2(f.fileno(), sys.stdout.fileno())
+ else:
+ saved_stdout = sys.stdout
+ sys.stdout = f
+ try:
+ self.curl.perform()
+ finally:
+ sys.stdout.flush()
+ if perform_dup:
+ os.fsync(sys.stdout.fileno())
+ os.dup2(saved_stdout_fd, sys.stdout.fileno())
+ os.close(saved_stdout_fd)
+ else:
+ sys.stdout = saved_stdout
+ f.seek(0)
+ body = f.read()
+ self.assertEqual('success', body)
--
1.7.1
From 9f92a0e68c031e26012db508c2a9482f8a466f6c Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Sat, 2 Mar 2013 02:23:27 -0500
Subject: [PATCH 017/149] Put stringio import into util
This way all tests can simply import stringio from util in one line.
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/util.py | 8 ++++++++
1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/tests/util.py b/tests/util.py
index a1a9978..891da44 100644
--- a/tests/util.py
+++ b/tests/util.py
@@ -4,6 +4,14 @@
import os, sys
+try:
+ from cStringIO import StringIO
+except ImportError:
+ try:
+ from StringIO import StringIO
+ except ImportError:
+ from io import StringIO
+
#
# prepare sys.path in case we are still in the build directory
# see also: distutils/command/build.py (build_platlib)
--
1.7.1
From 0289b4917a8510e174fe2237c28bdd219be3ef3f Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Sat, 2 Mar 2013 02:24:11 -0500
Subject: [PATCH 018/149] Debug test, ported from the old debug test
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/debug_test.py | 46 ++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 46 insertions(+), 0 deletions(-)
create mode 100644 tests/debug_test.py
diff --git a/tests/debug_test.py b/tests/debug_test.py
new file mode 100644
index 0000000..dd00719
--- /dev/null
+++ b/tests/debug_test.py
@@ -0,0 +1,46 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+import pycurl
+import unittest
+
+from . import app
+from . import runwsgi
+
+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380))
+
+class DebugTest(unittest.TestCase):
+ def setUp(self):
+ self.curl = pycurl.Curl()
+ self.debug_entries = []
+
+ def tearDown(self):
+ self.curl.close()
+
+ def debug_function(self, t, b):
+ self.debug_entries.append((t, b))
+
+ def test_perform_get_with_debug_function(self):
+ self.curl.setopt(pycurl.VERBOSE, 1)
+ self.curl.setopt(pycurl.DEBUGFUNCTION, self.debug_function)
+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
+ self.curl.perform()
+
+ # Some checks with no particular intent
+ self.check(0, 'About to connect')
+ self.check(0, 'Connected to localhost')
+ self.check(0, 'port 8380')
+ # request
+ self.check(2, 'GET /success HTTP/1.1')
+ # response
+ self.check(1, 'HTTP/1.0 200 OK')
+ self.check(1, 'Content-Length: 7')
+ # result
+ self.check(3, 'success')
+
+ def check(self, wanted_t, wanted_b):
+ for t, b in self.debug_entries:
+ if t == wanted_t and wanted_b in b:
+ return
+ assert False, "%d: %s not found in debug entries" % (wanted_t, wanted_b)
--
1.7.1
From 972cd4d7322aaa68074e44d5c9200a220fbb2b7e Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Sat, 2 Mar 2013 02:43:57 -0500
Subject: [PATCH 019/149] Write output to a stringio to avoid stdout spam
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/debug_test.py | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/tests/debug_test.py b/tests/debug_test.py
index dd00719..8005239 100644
--- a/tests/debug_test.py
+++ b/tests/debug_test.py
@@ -7,6 +7,7 @@ import unittest
from . import app
from . import runwsgi
+from . import util
setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380))
@@ -25,6 +26,8 @@ class DebugTest(unittest.TestCase):
self.curl.setopt(pycurl.VERBOSE, 1)
self.curl.setopt(pycurl.DEBUGFUNCTION, self.debug_function)
self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
+ sio = util.StringIO()
+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
self.curl.perform()
# Some checks with no particular intent
--
1.7.1
From ece77e7257bea16a9a9d4dd5efd948358974bc4a Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Sat, 2 Mar 2013 02:44:24 -0500
Subject: [PATCH 020/149] Use stringio from util in request test
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/request_test.py | 10 ++--------
1 files changed, 2 insertions(+), 8 deletions(-)
diff --git a/tests/request_test.py b/tests/request_test.py
index 127fbeb..afd8566 100644
--- a/tests/request_test.py
+++ b/tests/request_test.py
@@ -8,16 +8,10 @@ import tempfile
import pycurl
import unittest
import io
-try:
- from cStringIO import StringIO
-except ImportError:
- try:
- from StringIO import StringIO
- except ImportError:
- from io import StringIO
from . import app
from . import runwsgi
+from . import util
setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380))
@@ -42,7 +36,7 @@ class RequestTest(unittest.TestCase):
def test_perform_get_with_write_function(self):
self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
- sio = StringIO()
+ sio = util.StringIO()
self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
self.curl.perform()
self.assertEqual('success', sio.getvalue())
--
1.7.1
From aa359d2da90735e06d9c5dcf387e472e4b11f60d Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Sat, 2 Mar 2013 04:06:00 -0500
Subject: [PATCH 021/149] Add a test that writes response to a file
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/request_test.py | 9 +++++++++
1 files changed, 9 insertions(+), 0 deletions(-)
diff --git a/tests/request_test.py b/tests/request_test.py
index afd8566..7386a51 100644
--- a/tests/request_test.py
+++ b/tests/request_test.py
@@ -41,6 +41,15 @@ class RequestTest(unittest.TestCase):
self.curl.perform()
self.assertEqual('success', sio.getvalue())
+ def test_write_to_file(self):
+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
+ with tempfile.NamedTemporaryFile() as f:
+ self.curl.setopt(pycurl.WRITEFUNCTION, f.write)
+ self.curl.perform()
+ f.seek(0)
+ body = f.read()
+ self.assertEqual('success', body)
+
def test_perform_get_with_default_write_function(self):
self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
#with tempfile.NamedTemporaryFile() as f:
--
1.7.1
From 517c8f2dd4dc519fd733e049abab704e052866b6 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Sun, 3 Mar 2013 00:14:26 -0500
Subject: [PATCH 022/149] Account for the case of nose running no tests
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/runwsgi.py | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/tests/runwsgi.py b/tests/runwsgi.py
index d658614..7b39358 100644
--- a/tests/runwsgi.py
+++ b/tests/runwsgi.py
@@ -77,6 +77,8 @@ def app_runner_setup(*specs):
def teardown(self):
for server in self.servers:
- server.srv.shutdown()
+ # if no tests from module were run, there is no server to shut down
+ if hasattr(server, 'srv'):
+ server.srv.shutdown()
return [setup, teardown]
--
1.7.1
From cc68fffc8365c4f8e761a586d5e74beb5fd71473 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Sun, 3 Mar 2013 00:14:41 -0500
Subject: [PATCH 023/149] Split request test
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/default_write_function_test.py | 70 +++++++++++++++++++++++++++
tests/request_test.py | 87 ----------------------------------
tests/write_to_file_test.py | 29 +++++++++++
tests/write_to_stringio_test.py | 30 ++++++++++++
4 files changed, 129 insertions(+), 87 deletions(-)
create mode 100644 tests/default_write_function_test.py
delete mode 100644 tests/request_test.py
create mode 100644 tests/write_to_file_test.py
create mode 100644 tests/write_to_stringio_test.py
diff --git a/tests/default_write_function_test.py b/tests/default_write_function_test.py
new file mode 100644
index 0000000..2efc579
--- /dev/null
+++ b/tests/default_write_function_test.py
@@ -0,0 +1,70 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+import unittest
+import pycurl
+import sys
+import tempfile
+import io
+import os
+
+from . import app
+from . import runwsgi
+from . import util
+
+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380))
+
+class RequestTest(unittest.TestCase):
+ def setUp(self):
+ self.curl = pycurl.Curl()
+
+ def tearDown(self):
+ self.curl.close()
+
+ def test_perform_get(self):
+ # This test performs a GET request without doing anything else.
+ # Unfortunately, the default curl behavior is to print response
+ # body to standard output, which spams test output.
+ # As a result this test is commented out. Uncomment for debugging.
+ # test_perform_get_with_default_write_function is the test
+ # which exercises default curl write handler.
+
+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
+ self.curl.perform()
+
+ def test_perform_get_with_default_write_function(self):
+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
+ #with tempfile.NamedTemporaryFile() as f:
+ with open('w', 'w+') as f:
+ # nose output capture plugin replaces sys.stdout with a StringIO
+ # instance. We want to redirect the underlying file descriptor
+ # anyway because underlying C code uses it.
+ # But keep track of whether we replace sys.stdout.
+ perform_dup = False
+ if hasattr(sys.stdout, 'fileno'):
+ try:
+ sys.stdout.fileno()
+ perform_dup = True
+ except io.UnsupportedOperation:
+ # stdout is a StringIO
+ pass
+ if perform_dup:
+ saved_stdout_fd = os.dup(sys.stdout.fileno())
+ os.dup2(f.fileno(), sys.stdout.fileno())
+ else:
+ saved_stdout = sys.stdout
+ sys.stdout = f
+ try:
+ self.curl.perform()
+ finally:
+ sys.stdout.flush()
+ if perform_dup:
+ os.fsync(sys.stdout.fileno())
+ os.dup2(saved_stdout_fd, sys.stdout.fileno())
+ os.close(saved_stdout_fd)
+ else:
+ sys.stdout = saved_stdout
+ f.seek(0)
+ body = f.read()
+ self.assertEqual('success', body)
diff --git a/tests/request_test.py b/tests/request_test.py
deleted file mode 100644
index 7386a51..0000000
--- a/tests/request_test.py
+++ /dev/null
@@ -1,87 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-# vi:ts=4:et
-
-import os
-import sys
-import tempfile
-import pycurl
-import unittest
-import io
-
-from . import app
-from . import runwsgi
-from . import util
-
-setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380))
-
-class RequestTest(unittest.TestCase):
- def setUp(self):
- self.curl = pycurl.Curl()
-
- def tearDown(self):
- self.curl.close()
-
- def test_perform_get(self):
- # This test performs a GET request without doing anything else.
- # Unfortunately, the default curl behavior is to print response
- # body to standard output, which spams test output.
- # As a result this test is commented out. Uncomment for debugging.
- # test_perform_get_with_default_write_function is the test
- # which exercises default curl write handler.
- return
-
- self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
- self.curl.perform()
-
- def test_perform_get_with_write_function(self):
- self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
- sio = util.StringIO()
- self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
- self.curl.perform()
- self.assertEqual('success', sio.getvalue())
-
- def test_write_to_file(self):
- self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
- with tempfile.NamedTemporaryFile() as f:
- self.curl.setopt(pycurl.WRITEFUNCTION, f.write)
- self.curl.perform()
- f.seek(0)
- body = f.read()
- self.assertEqual('success', body)
-
- def test_perform_get_with_default_write_function(self):
- self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
- #with tempfile.NamedTemporaryFile() as f:
- with open('w', 'w+') as f:
- # nose output capture plugin replaces sys.stdout with a StringIO
- # instance. We want to redirect the underlying file descriptor
- # anyway because underlying C code uses it.
- # But keep track of whether we replace sys.stdout.
- perform_dup = False
- if hasattr(sys.stdout, 'fileno'):
- try:
- sys.stdout.fileno()
- perform_dup = True
- except io.UnsupportedOperation:
- # stdout is a StringIO
- pass
- if perform_dup:
- saved_stdout_fd = os.dup(sys.stdout.fileno())
- os.dup2(f.fileno(), sys.stdout.fileno())
- else:
- saved_stdout = sys.stdout
- sys.stdout = f
- try:
- self.curl.perform()
- finally:
- sys.stdout.flush()
- if perform_dup:
- os.fsync(sys.stdout.fileno())
- os.dup2(saved_stdout_fd, sys.stdout.fileno())
- os.close(saved_stdout_fd)
- else:
- sys.stdout = saved_stdout
- f.seek(0)
- body = f.read()
- self.assertEqual('success', body)
diff --git a/tests/write_to_file_test.py b/tests/write_to_file_test.py
new file mode 100644
index 0000000..6528f81
--- /dev/null
+++ b/tests/write_to_file_test.py
@@ -0,0 +1,29 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+import unittest
+import pycurl
+import tempfile
+
+from . import app
+from . import runwsgi
+from . import util
+
+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380))
+
+class RequestTest(unittest.TestCase):
+ def setUp(self):
+ self.curl = pycurl.Curl()
+
+ def tearDown(self):
+ self.curl.close()
+
+ def test_get_to_file(self):
+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
+ with tempfile.NamedTemporaryFile() as f:
+ self.curl.setopt(pycurl.WRITEFUNCTION, f.write)
+ self.curl.perform()
+ f.seek(0)
+ body = f.read()
+ self.assertEqual('success', body)
diff --git a/tests/write_to_stringio_test.py b/tests/write_to_stringio_test.py
new file mode 100644
index 0000000..fd1c28d
--- /dev/null
+++ b/tests/write_to_stringio_test.py
@@ -0,0 +1,30 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+import os
+import sys
+import tempfile
+import pycurl
+import unittest
+import io
+
+from . import app
+from . import runwsgi
+from . import util
+
+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380))
+
+class WriteToStringioTest(unittest.TestCase):
+ def setUp(self):
+ self.curl = pycurl.Curl()
+
+ def tearDown(self):
+ self.curl.close()
+
+ def test_get(self):
+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
+ sio = util.StringIO()
+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
+ self.curl.perform()
+ self.assertEqual('success', sio.getvalue())
--
1.7.1
From f7055358a57d0fa9bcef3f0ad38a63ff5422178c Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Sun, 3 Mar 2013 00:22:05 -0500
Subject: [PATCH 024/149] Need to flush when using default write function
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/default_write_function_test.py | 5 ++++-
1 files changed, 4 insertions(+), 1 deletions(-)
diff --git a/tests/default_write_function_test.py b/tests/default_write_function_test.py
index 2efc579..afbba33 100644
--- a/tests/default_write_function_test.py
+++ b/tests/default_write_function_test.py
@@ -32,6 +32,9 @@ class RequestTest(unittest.TestCase):
self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
self.curl.perform()
+ # If this flush is not done, stdout output bleeds into the next test
+ # that is executed
+ sys.stdout.flush()
def test_perform_get_with_default_write_function(self):
self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
@@ -57,8 +60,8 @@ class RequestTest(unittest.TestCase):
sys.stdout = f
try:
self.curl.perform()
- finally:
sys.stdout.flush()
+ finally:
if perform_dup:
os.fsync(sys.stdout.fileno())
os.dup2(saved_stdout_fd, sys.stdout.fileno())
--
1.7.1
From bb6edcd11b75949aa4c7b7513ce70f8a16a8a857 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Sun, 3 Mar 2013 00:34:39 -0500
Subject: [PATCH 025/149] Trying to get the default write function test to work with nose output capture, but no luck so far
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/default_write_function_test.py | 52 +++++++++++++++++----------------
1 files changed, 27 insertions(+), 25 deletions(-)
diff --git a/tests/default_write_function_test.py b/tests/default_write_function_test.py
index afbba33..853992b 100644
--- a/tests/default_write_function_test.py
+++ b/tests/default_write_function_test.py
@@ -15,6 +15,8 @@ from . import util
setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380))
+STDOUT_FD_NUM = 1
+
class RequestTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
@@ -33,41 +35,41 @@ class RequestTest(unittest.TestCase):
self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
self.curl.perform()
# If this flush is not done, stdout output bleeds into the next test
- # that is executed
+ # that is executed (without nose output capture)
sys.stdout.flush()
+ os.fsync(STDOUT_FD_NUM)
- def test_perform_get_with_default_write_function(self):
+ # I have a really hard time getting this to work with nose output capture
+ def skip_test_perform_get_with_default_write_function(self):
self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
- #with tempfile.NamedTemporaryFile() as f:
- with open('w', 'w+') as f:
+ with tempfile.NamedTemporaryFile() as f:
+ #with open('w', 'w+') as f:
# nose output capture plugin replaces sys.stdout with a StringIO
# instance. We want to redirect the underlying file descriptor
# anyway because underlying C code uses it.
- # But keep track of whether we replace sys.stdout.
- perform_dup = False
- if hasattr(sys.stdout, 'fileno'):
- try:
- sys.stdout.fileno()
- perform_dup = True
- except io.UnsupportedOperation:
- # stdout is a StringIO
- pass
- if perform_dup:
- saved_stdout_fd = os.dup(sys.stdout.fileno())
- os.dup2(f.fileno(), sys.stdout.fileno())
- else:
- saved_stdout = sys.stdout
- sys.stdout = f
+ # Therefore:
+ # 1. Use file descriptor 1 rather than sys.stdout.fileno() to
+ # reference the standard output file descriptor.
+ # 2. We do not touch sys.stdout. This means anything written to
+ # sys.stdout will be captured by nose, and not make it to our code.
+ # But the output we care about happens at libcurl level, below
+ # nose, therefore this is fine.
+ saved_stdout_fd = os.dup(STDOUT_FD_NUM)
+ os.dup2(f.fileno(), STDOUT_FD_NUM)
+ #os.dup2(1, 100)
+ #os.dup2(2, 1)
+ # We also need to flush the output that libcurl wrote to stdout.
+ # Since sys.stdout might be nose's StringIO instance, open the
+ # stdout file descriptor manually.
+
try:
self.curl.perform()
sys.stdout.flush()
finally:
- if perform_dup:
- os.fsync(sys.stdout.fileno())
- os.dup2(saved_stdout_fd, sys.stdout.fileno())
- os.close(saved_stdout_fd)
- else:
- sys.stdout = saved_stdout
+ os.fsync(STDOUT_FD_NUM)
+ os.dup2(saved_stdout_fd, STDOUT_FD_NUM)
+ os.close(saved_stdout_fd)
+ #os.dup2(100, 1)
f.seek(0)
body = f.read()
self.assertEqual('success', body)
--
1.7.1
From 83a7d49e7e27f499e9d400a1af09e07fcc595c2f Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Sun, 3 Mar 2013 00:35:25 -0500
Subject: [PATCH 026/149] Fix test class names
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/default_write_function_test.py | 2 +-
tests/write_to_file_test.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/default_write_function_test.py b/tests/default_write_function_test.py
index 853992b..a0de66e 100644
--- a/tests/default_write_function_test.py
+++ b/tests/default_write_function_test.py
@@ -17,7 +17,7 @@ setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380))
STDOUT_FD_NUM = 1
-class RequestTest(unittest.TestCase):
+class DefaultWriteFunctionTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
diff --git a/tests/write_to_file_test.py b/tests/write_to_file_test.py
index 6528f81..67c9c63 100644
--- a/tests/write_to_file_test.py
+++ b/tests/write_to_file_test.py
@@ -12,7 +12,7 @@ from . import util
setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380))
-class RequestTest(unittest.TestCase):
+class WriteToFileTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
--
1.7.1
From 44ff45d8644789a098ea68b196b5f5cea5fbfd35 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Sun, 3 Mar 2013 00:36:34 -0500
Subject: [PATCH 027/149] Delete unused imports
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/write_to_stringio_test.py | 3 ---
1 files changed, 0 insertions(+), 3 deletions(-)
diff --git a/tests/write_to_stringio_test.py b/tests/write_to_stringio_test.py
index fd1c28d..018800d 100644
--- a/tests/write_to_stringio_test.py
+++ b/tests/write_to_stringio_test.py
@@ -2,9 +2,6 @@
# -*- coding: iso-8859-1 -*-
# vi:ts=4:et
-import os
-import sys
-import tempfile
import pycurl
import unittest
import io
--
1.7.1
From f14cdea639d6976f8dd8077be28c4452d4c8d1cf Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Sun, 3 Mar 2013 00:41:35 -0500
Subject: [PATCH 028/149] Test for header function
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/header_function_test.py | 50 +++++++++++++++++++++++++++++++++++++++++
1 files changed, 50 insertions(+), 0 deletions(-)
create mode 100644 tests/header_function_test.py
diff --git a/tests/header_function_test.py b/tests/header_function_test.py
new file mode 100644
index 0000000..bfe7173
--- /dev/null
+++ b/tests/header_function_test.py
@@ -0,0 +1,50 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+import pycurl
+import unittest
+import io
+import time as _time
+
+from . import app
+from . import runwsgi
+from . import util
+
+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380))
+
+class HeaderFunctionTest(unittest.TestCase):
+ def setUp(self):
+ self.curl = pycurl.Curl()
+ self.header_lines = []
+
+ def tearDown(self):
+ self.curl.close()
+
+ def header_function(self, line):
+ self.header_lines.append(line)
+
+ def test_get(self):
+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
+ sio = util.StringIO()
+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
+ self.curl.setopt(pycurl.HEADERFUNCTION, self.header_function)
+ self.curl.perform()
+ self.assertEqual('success', sio.getvalue())
+
+ assert len(self.header_lines) > 0
+ self.assertEqual("HTTP/1.0 200 OK\r\n", self.header_lines[0])
+ # day of week
+ todays_day = _time.strftime('%a')
+ # Date: Sun, 03 Mar 2013 05:38:12 GMT\r\n
+ self.check('Date: %s' % todays_day)
+ # Server: WSGIServer/0.1 Python/2.7.3\r\n
+ self.check('Server: WSGIServer')
+ self.check('Content-Length: 7')
+ self.check('Content-Type: text/html')
+
+ def check(self, wanted_text):
+ for line in self.header_lines:
+ if wanted_text in line:
+ return
+ assert False, "%s not found in header lines" % wanted_text
--
1.7.1
From 2491ddd3f028059b4e86338ad64413de75db28af Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Sun, 3 Mar 2013 01:35:21 -0500
Subject: [PATCH 029/149] FTP test
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/ftp_test.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 48 insertions(+), 0 deletions(-)
create mode 100644 tests/ftp_test.py
diff --git a/tests/ftp_test.py b/tests/ftp_test.py
new file mode 100644
index 0000000..d215b6e
--- /dev/null
+++ b/tests/ftp_test.py
@@ -0,0 +1,48 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+import pycurl
+import unittest
+
+from . import util
+
+class FtpTest(unittest.TestCase):
+ def setUp(self):
+ self.curl = pycurl.Curl()
+
+ def tearDown(self):
+ self.curl.close()
+
+ def test_get_ftp(self):
+ self.curl.setopt(pycurl.URL, 'ftp://localhost:8921')
+ sio = util.StringIO()
+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
+ self.curl.perform()
+
+ result = sio.getvalue()
+ assert 'README' in result
+ assert 'bin -> usr/bin' in result
+
+ # XXX this test needs to be fixed
+ def test_quote(self):
+ self.curl.setopt(pycurl.URL, 'ftp://localhost:8921')
+ sio = util.StringIO()
+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
+ self.curl.setopt(pycurl.QUOTE, ['CWD pub'])
+ self.curl.perform()
+
+ result = sio.getvalue()
+ assert 'README' in result
+ assert 'bin -> usr/bin' in result
+
+ def test_epsv(self):
+ self.curl.setopt(pycurl.URL, 'ftp://localhost:8921')
+ sio = util.StringIO()
+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
+ self.curl.setopt(pycurl.FTP_USE_EPSV, 1)
+ self.curl.perform()
+
+ result = sio.getvalue()
+ assert 'README' in result
+ assert 'bin -> usr/bin' in result
--
1.7.1
From ead7f2743a7bd6b497d994ca11dc823a144d50d7 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Sun, 3 Mar 2013 01:44:22 -0500
Subject: [PATCH 030/149] Getinfo test
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/getinfo_test.py | 43 +++++++++++++++++++++++++++++++++++++++++++
1 files changed, 43 insertions(+), 0 deletions(-)
create mode 100644 tests/getinfo_test.py
diff --git a/tests/getinfo_test.py b/tests/getinfo_test.py
new file mode 100644
index 0000000..39dff2f
--- /dev/null
+++ b/tests/getinfo_test.py
@@ -0,0 +1,43 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+import pycurl
+import unittest
+
+from . import app
+from . import runwsgi
+from . import util
+
+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380))
+
+class GetinfoTest(unittest.TestCase):
+ def setUp(self):
+ self.curl = pycurl.Curl()
+
+ def tearDown(self):
+ self.curl.close()
+
+ def test_getinfo(self):
+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
+ sio = util.StringIO()
+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
+ self.curl.perform()
+ self.assertEqual('success', sio.getvalue())
+
+ self.assertEqual(200, self.curl.getinfo(pycurl.HTTP_CODE))
+ assert type(self.curl.getinfo(pycurl.TOTAL_TIME)) is float
+ assert self.curl.getinfo(pycurl.TOTAL_TIME) > 0
+ assert self.curl.getinfo(pycurl.TOTAL_TIME) < 1
+ assert type(self.curl.getinfo(pycurl.SPEED_DOWNLOAD)) is float
+ assert self.curl.getinfo(pycurl.SPEED_DOWNLOAD) > 0
+ self.assertEqual(7, self.curl.getinfo(pycurl.SIZE_DOWNLOAD))
+ self.assertEqual('http://localhost:8380/success', self.curl.getinfo(pycurl.EFFECTIVE_URL))
+ self.assertEqual('text/html; charset=utf-8', self.curl.getinfo(pycurl.CONTENT_TYPE).lower())
+ assert type(self.curl.getinfo(pycurl.NAMELOOKUP_TIME)) is float
+ assert self.curl.getinfo(pycurl.NAMELOOKUP_TIME) > 0
+ assert self.curl.getinfo(pycurl.NAMELOOKUP_TIME) < 1
+ self.assertEqual(0, self.curl.getinfo(pycurl.REDIRECT_TIME))
+ self.assertEqual(0, self.curl.getinfo(pycurl.REDIRECT_COUNT))
+ # time not requested
+ self.assertEqual(-1, self.curl.getinfo(pycurl.INFO_FILETIME))
--
1.7.1
From 7c48535bef19bf13a9d8aef377d4465490a588f2 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Sun, 3 Mar 2013 02:05:30 -0500
Subject: [PATCH 031/149] Ported internals test
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/internals_test.py | 224 +++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 224 insertions(+), 0 deletions(-)
create mode 100644 tests/internals_test.py
diff --git a/tests/internals_test.py b/tests/internals_test.py
new file mode 100644
index 0000000..34d4eb8
--- /dev/null
+++ b/tests/internals_test.py
@@ -0,0 +1,224 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+import pycurl
+import unittest
+from .util import StringIO
+try:
+ import cPickle
+except ImportError:
+ cPickle = None
+import pickle
+import gc
+import copy
+
+class InternalsTest(unittest.TestCase):
+ def setUp(self):
+ self.curl = pycurl.Curl()
+
+ def tearDown(self):
+ self.curl.close()
+ del self.curl
+
+ # /***********************************************************************
+ # // test misc
+ # ************************************************************************/
+
+ def test_constant_aliasing(self):
+ assert self.curl.URL is pycurl.URL
+
+ # /***********************************************************************
+ # // test handles
+ # ************************************************************************/
+
+ def test_remove_invalid_handle(self):
+ m = pycurl.CurlMulti()
+ try:
+ m.remove_handle(self.curl)
+ except pycurl.error:
+ pass
+ else:
+ assert False, "No exception when trying to remove a handle that is not in CurlMulti"
+ del m
+
+ def test_remove_invalid_closed_handle(self):
+ m = pycurl.CurlMulti()
+ c = pycurl.Curl()
+ c.close()
+ m.remove_handle(c)
+ del m, c
+
+ def test_add_closed_handle(self):
+ m = pycurl.CurlMulti()
+ c = pycurl.Curl()
+ c.close()
+ try:
+ m.add_handle(c)
+ except pycurl.error:
+ pass
+ else:
+ assert 0, "No exception when trying to add a close handle to CurlMulti"
+ m.close()
+ del m, c
+
+ def test_add_handle_twice(self):
+ m = pycurl.CurlMulti()
+ m.add_handle(self.curl)
+ try:
+ m.add_handle(self.curl)
+ except pycurl.error:
+ pass
+ else:
+ assert 0, "No exception when trying to add the same handle twice"
+ del m
+
+ def test_add_handle_on_multiple_stacks(self):
+ m1 = pycurl.CurlMulti()
+ m2 = pycurl.CurlMulti()
+ m1.add_handle(self.curl)
+ try:
+ m2.add_handle(self.curl)
+ except pycurl.error:
+ pass
+ else:
+ assert 0, "No exception when trying to add the same handle on multiple stacks"
+ del m1, m2
+
+ def test_move_handle(self):
+ m1 = pycurl.CurlMulti()
+ m2 = pycurl.CurlMulti()
+ m1.add_handle(self.curl)
+ m1.remove_handle(self.curl)
+ m2.add_handle(self.curl)
+ del m1, m2
+
+ # /***********************************************************************
+ # // test copying and pickling - copying and pickling of
+ # // instances of Curl and CurlMulti is not allowed
+ # ************************************************************************/
+
+ def test_copy_curl(self):
+ try:
+ copy.copy(self.curl)
+ # python 2 raises copy.Error, python 3 raises TypeError
+ except (copy.Error, TypeError):
+ pass
+ else:
+ assert False, "No exception when trying to copy a Curl handle"
+
+ def test_copy_multi(self):
+ m = pycurl.CurlMulti()
+ try:
+ copy.copy(m)
+ except (copy.Error, TypeError):
+ pass
+ else:
+ assert False, "No exception when trying to copy a CurlMulti handle"
+
+ def test_pickle_curl(self):
+ fp = StringIO()
+ p = pickle.Pickler(fp, 1)
+ try:
+ p.dump(self.curl)
+ # python 2 raises pickle.PicklingError, python 3 raises TypeError
+ except (pickle.PicklingError, TypeError):
+ pass
+ else:
+ assert 0, "No exception when trying to pickle a Curl handle"
+ del fp, p
+
+ def test_pickle_multi(self):
+ m = pycurl.CurlMulti()
+ fp = StringIO()
+ p = pickle.Pickler(fp, 1)
+ try:
+ p.dump(m)
+ except (pickle.PicklingError, TypeError):
+ pass
+ else:
+ assert 0, "No exception when trying to pickle a CurlMulti handle"
+ del m, fp, p
+
+ if cPickle is not None:
+ def test_cpickle_curl(self):
+ fp = StringIO()
+ p = cPickle.Pickler(fp, 1)
+ try:
+ p.dump(self.curl)
+ except cPickle.PicklingError:
+ pass
+ else:
+ assert 0, "No exception when trying to pickle a Curl handle via cPickle"
+ del fp, p
+
+ def test_cpickle_multi(self):
+ m = pycurl.CurlMulti()
+ fp = StringIO()
+ p = cPickle.Pickler(fp, 1)
+ try:
+ p.dump(m)
+ except cPickle.PicklingError:
+ pass
+ else:
+ assert 0, "No exception when trying to pickle a CurlMulti handle via cPickle"
+ del m, fp, p
+
+ # /***********************************************************************
+ # // test refcounts
+ # ************************************************************************/
+
+ # basic check of reference counting (use a memory checker like valgrind)
+ def test_reference_counting(self):
+ c = pycurl.Curl()
+ m = pycurl.CurlMulti()
+ m.add_handle(c)
+ del m
+ m = pycurl.CurlMulti()
+ c.close()
+ del m, c
+
+ def test_cyclic_gc(self):
+ gc.collect()
+ c = pycurl.Curl()
+ c.m = pycurl.CurlMulti()
+ c.m.add_handle(c)
+ # create some nasty cyclic references
+ c.c = c
+ c.c.c1 = c
+ c.c.c2 = c
+ c.c.c3 = c.c
+ c.c.c4 = c.m
+ c.m.c = c
+ c.m.m = c.m
+ c.m.c = c
+ # delete
+ gc.collect()
+ flags = gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE
+ # python 3 has no DEBUG_OBJECTS
+ #if hasattr(gc, 'DEBUG_OBJECTS'):
+ #flags |= gc.DEBUG_OBJECTS
+ #if opts.verbose >= 1:
+ #flags = flags | gc.DEBUG_STATS
+ gc.set_debug(flags)
+ gc.collect()
+ ##print gc.get_referrers(c)
+ ##print gc.get_objects()
+ #if opts.verbose >= 1:
+ #print("Tracked objects:", len(gc.get_objects()))
+ # The `del' below should delete these 4 objects:
+ # Curl + internal dict, CurlMulti + internal dict
+ del c
+ gc.collect()
+ #if opts.verbose >= 1:
+ #print("Tracked objects:", len(gc.get_objects()))
+
+ def test_refcounting_bug_in_reset(self):
+ try:
+ range_generator = xrange
+ except NameError:
+ range_generator = range
+ # Ensure that the refcounting error in "reset" is fixed:
+ for i in range_generator(100000):
+ c = pycurl.Curl()
+ c.reset()
--
1.7.1
From c7b8f39437e953deb8af1b17f366a6242db28d5d Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Sun, 3 Mar 2013 02:16:10 -0500
Subject: [PATCH 032/149] Make cyclic gc test actually check that cycles are collected
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/internals_test.py | 9 +++++++++
1 files changed, 9 insertions(+), 0 deletions(-)
diff --git a/tests/internals_test.py b/tests/internals_test.py
index 34d4eb8..fb451df 100644
--- a/tests/internals_test.py
+++ b/tests/internals_test.py
@@ -12,6 +12,7 @@ except ImportError:
import pickle
import gc
import copy
+import re
class InternalsTest(unittest.TestCase):
def setUp(self):
@@ -179,6 +180,7 @@ class InternalsTest(unittest.TestCase):
del m, c
def test_cyclic_gc(self):
+ regexp = re.compile(r'at (0x\d+)')
gc.collect()
c = pycurl.Curl()
c.m = pycurl.CurlMulti()
@@ -206,10 +208,17 @@ class InternalsTest(unittest.TestCase):
##print gc.get_objects()
#if opts.verbose >= 1:
#print("Tracked objects:", len(gc.get_objects()))
+ match = regexp.search(repr(c))
+ assert match is not None
+ address = match.group(1)
# The `del' below should delete these 4 objects:
# Curl + internal dict, CurlMulti + internal dict
del c
gc.collect()
+ objects = gc.get_objects()
+ search = 'at %s' % address
+ for object in objects:
+ assert search not in repr(object)
#if opts.verbose >= 1:
#print("Tracked objects:", len(gc.get_objects()))
--
1.7.1
From 6fa36d3a070e784fc679427a6c56725ee30375e8 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Sun, 3 Mar 2013 03:00:20 -0500
Subject: [PATCH 033/149] Ported memleak test
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/memleak_test.py | 59 +++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 59 insertions(+), 0 deletions(-)
create mode 100644 tests/memleak_test.py
diff --git a/tests/memleak_test.py b/tests/memleak_test.py
new file mode 100644
index 0000000..6e9f76c
--- /dev/null
+++ b/tests/memleak_test.py
@@ -0,0 +1,59 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+import pycurl
+import unittest
+import gc
+import re
+
+class MemleakTest(unittest.TestCase):
+ def test_collection(self):
+ regexp = re.compile(r'at (0x\d+)')
+
+ gc.collect()
+ flags = gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE
+ # python 3 has no DEBUG_OBJECTS
+ #if hasattr(gc, 'DEBUG_OBJECTS'):
+ #flags |= gc.DEBUG_OBJECTS
+ #if 1:
+ #flags = flags | gc.DEBUG_STATS
+ #gc.set_debug(flags)
+ gc.collect()
+
+ #print("Tracked objects:", len(gc.get_objects()))
+
+ multi = pycurl.CurlMulti()
+ t = []
+ searches = []
+ for a in range(100):
+ curl = pycurl.Curl()
+ multi.add_handle(curl)
+ t.append(curl)
+
+ match = regexp.search(repr(curl))
+ assert match is not None
+ searches.append(match.group(1))
+ match = regexp.search(repr(multi))
+ assert match
+ searches.append(match.group(1))
+
+ #print("Tracked objects:", len(gc.get_objects()))
+
+ for curl in t:
+ curl.close()
+ multi.remove_handle(curl)
+
+ #print("Tracked objects:", len(gc.get_objects()))
+
+ del curl
+ del t
+ del multi
+
+ #print("Tracked objects:", len(gc.get_objects()))
+ gc.collect()
+ #print("Tracked objects:", len(gc.get_objects()))
+
+ objects = gc.get_objects()
+ for search in searches:
+ assert 'at %s' % search not in repr(object)
--
1.7.1
From cef807dfde5e1ca931947ec37d0171227899e72f Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Sun, 3 Mar 2013 03:06:26 -0500
Subject: [PATCH 034/149] Ported the first multi test
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/multi_test.py | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 56 insertions(+), 0 deletions(-)
create mode 100644 tests/multi_test.py
diff --git a/tests/multi_test.py b/tests/multi_test.py
new file mode 100644
index 0000000..4c0bdaf
--- /dev/null
+++ b/tests/multi_test.py
@@ -0,0 +1,56 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+import pycurl
+import unittest
+
+from . import app
+from . import runwsgi
+from . import util
+
+setup_module_1, teardown_module_1 = runwsgi.app_runner_setup((app.app, 8380))
+setup_module_2, teardown_module_2 = runwsgi.app_runner_setup((app.app, 8381))
+
+def setup_module(mod):
+ setup_module_1(mod)
+ setup_module_2(mod)
+
+def teardown_module(mod):
+ teardown_module_2(mod)
+ teardown_module_1(mod)
+
+class MultiTest(unittest.TestCase):
+ def test_multi(self):
+ io1 = util.StringIO()
+ io2 = util.StringIO()
+ m = pycurl.CurlMulti()
+ m.handles = []
+ c1 = pycurl.Curl()
+ c2 = pycurl.Curl()
+ c1.setopt(c1.URL, 'http://localhost:8380/success')
+ c1.setopt(c1.WRITEFUNCTION, io1.write)
+ c2.setopt(c2.URL, 'http://localhost:8381/success')
+ c2.setopt(c1.WRITEFUNCTION, io2.write)
+ m.add_handle(c1)
+ m.add_handle(c2)
+ m.handles.append(c1)
+ m.handles.append(c2)
+
+ num_handles = len(m.handles)
+ while num_handles:
+ while 1:
+ ret, num_handles = m.perform()
+ if ret != pycurl.E_CALL_MULTI_PERFORM:
+ break
+ m.select(1.0)
+
+ m.remove_handle(c2)
+ m.remove_handle(c1)
+ del m.handles
+ m.close()
+ c1.close()
+ c2.close()
+
+ self.assertEqual('success', io1.getvalue())
+ self.assertEqual('success', io2.getvalue())
--
1.7.1
From 5a9488e0e5f75e70faf37b419819ce18878f29ed Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Sun, 3 Mar 2013 12:55:42 -0500
Subject: [PATCH 035/149] Port multi2 test
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/app.py | 8 +++++++
tests/multi_test.py | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 62 insertions(+), 0 deletions(-)
diff --git a/tests/app.py b/tests/app.py
index b173fd6..3b9303b 100644
--- a/tests/app.py
+++ b/tests/app.py
@@ -5,3 +5,11 @@ app = bottle.Bottle()
@app.route('/success')
def ok():
return 'success'
+
+@app.route('/status/403')
+def forbidden():
+ bottle.abort(403, 'forbidden')
+
+@app.route('/status/404')
+def not_found():
+ bottle.abort(404, 'not found')
diff --git a/tests/multi_test.py b/tests/multi_test.py
index 4c0bdaf..fd96e51 100644
--- a/tests/multi_test.py
+++ b/tests/multi_test.py
@@ -11,12 +11,15 @@ from . import util
setup_module_1, teardown_module_1 = runwsgi.app_runner_setup((app.app, 8380))
setup_module_2, teardown_module_2 = runwsgi.app_runner_setup((app.app, 8381))
+setup_module_3, teardown_module_3 = runwsgi.app_runner_setup((app.app, 8382))
def setup_module(mod):
setup_module_1(mod)
setup_module_2(mod)
+ setup_module_3(mod)
def teardown_module(mod):
+ teardown_module_3(mod)
teardown_module_2(mod)
teardown_module_1(mod)
@@ -54,3 +57,54 @@ class MultiTest(unittest.TestCase):
self.assertEqual('success', io1.getvalue())
self.assertEqual('success', io2.getvalue())
+
+ def test_multi_status_codes(self):
+ # init
+ m = pycurl.CurlMulti()
+ m.handles = []
+ urls = [
+ 'http://localhost:8380/success',
+ 'http://localhost:8381/status/403',
+ 'http://localhost:8382/status/404',
+ ]
+ for url in urls:
+ c = pycurl.Curl()
+ # save info in standard Python attributes
+ c.url = url.rstrip()
+ c.body = util.StringIO()
+ c.http_code = -1
+ m.handles.append(c)
+ # pycurl API calls
+ c.setopt(c.URL, c.url)
+ c.setopt(c.WRITEFUNCTION, c.body.write)
+ m.add_handle(c)
+
+ # get data
+ num_handles = len(m.handles)
+ while num_handles:
+ while 1:
+ ret, num_handles = m.perform()
+ if ret != pycurl.E_CALL_MULTI_PERFORM:
+ break
+ # currently no more I/O is pending, could do something in the meantime
+ # (display a progress bar, etc.)
+ m.select(0.1)
+
+ # close handles
+ for c in m.handles:
+ # save info in standard Python attributes
+ c.http_code = c.getinfo(c.HTTP_CODE)
+ # pycurl API calls
+ m.remove_handle(c)
+ c.close()
+ m.close()
+
+ # check result
+ self.assertEqual('success', m.handles[0].body.getvalue())
+ self.assertEqual(200, m.handles[0].http_code)
+ # bottle generated response body
+ assert 'Error 403: Forbidden' in m.handles[1].body.getvalue()
+ self.assertEqual(403, m.handles[1].http_code)
+ # bottle generated response body
+ assert 'Error 404: Not Found' in m.handles[2].body.getvalue()
+ self.assertEqual(404, m.handles[2].http_code)
--
1.7.1
From ddeba20f213aea07a2491df171b899b2453de70b Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Sun, 3 Mar 2013 14:05:49 -0500
Subject: [PATCH 036/149] Porting multi3 test
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/multi_test.py | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 71 insertions(+), 0 deletions(-)
diff --git a/tests/multi_test.py b/tests/multi_test.py
index fd96e51..1c13f70 100644
--- a/tests/multi_test.py
+++ b/tests/multi_test.py
@@ -108,3 +108,74 @@ class MultiTest(unittest.TestCase):
# bottle generated response body
assert 'Error 404: Not Found' in m.handles[2].body.getvalue()
self.assertEqual(404, m.handles[2].http_code)
+
+ def test_adding_closed_handle(self):
+ # init
+ m = pycurl.CurlMulti()
+ m.handles = []
+ urls = [
+ 'http://localhost:8380/success',
+ 'http://localhost:8381/status/403',
+ 'http://localhost:8382/status/404',
+ ]
+ for url in urls:
+ c = pycurl.Curl()
+ # save info in standard Python attributes
+ c.url = url
+ c.body = util.StringIO()
+ c.http_code = -1
+ c.debug = 0
+ m.handles.append(c)
+ # pycurl API calls
+ c.setopt(c.URL, c.url)
+ c.setopt(c.WRITEFUNCTION, c.body.write)
+ m.add_handle(c)
+
+ # debug - close a handle
+ c = m.handles[2]
+ c.debug = 1
+ c.close()
+
+ # get data
+ num_handles = len(m.handles)
+ while num_handles:
+ while 1:
+ ret, num_handles = m.perform()
+ if ret != pycurl.E_CALL_MULTI_PERFORM:
+ break
+ # currently no more I/O is pending, could do something in the meantime
+ # (display a progress bar, etc.)
+ m.select(0.1)
+
+ # close handles
+ for c in m.handles:
+ # save info in standard Python attributes
+ try:
+ c.http_code = c.getinfo(c.HTTP_CODE)
+ except pycurl.error:
+ # handle already closed - see debug above
+ assert c.debug
+ c.http_code = -1
+ # pycurl API calls
+ if 0:
+ m.remove_handle(c)
+ c.close()
+ elif 0:
+ # in the C API this is the wrong calling order, but pycurl
+ # handles this automatically
+ c.close()
+ m.remove_handle(c)
+ else:
+ # actually, remove_handle is called automatically on close
+ c.close()
+ m.close()
+
+ # check result
+ self.assertEqual('success', m.handles[0].body.getvalue())
+ self.assertEqual(200, m.handles[0].http_code)
+ # bottle generated response body
+ assert 'Error 403: Forbidden' in m.handles[1].body.getvalue()
+ self.assertEqual(403, m.handles[1].http_code)
+ # bottle generated response body
+ self.assertEqual('', m.handles[2].body.getvalue())
+ self.assertEqual(-1, m.handles[2].http_code)
--
1.7.1
From bd01383371db110534ea39c4ce35563d63a21f73 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Sun, 3 Mar 2013 14:15:13 -0500
Subject: [PATCH 037/149] Exercise all 3 possibilites in ported multi3 test
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/multi_test.py | 37 +++++++++++++++++++++++++------------
1 files changed, 25 insertions(+), 12 deletions(-)
diff --git a/tests/multi_test.py b/tests/multi_test.py
index 1c13f70..e1fec05 100644
--- a/tests/multi_test.py
+++ b/tests/multi_test.py
@@ -109,7 +109,7 @@ class MultiTest(unittest.TestCase):
assert 'Error 404: Not Found' in m.handles[2].body.getvalue()
self.assertEqual(404, m.handles[2].http_code)
- def test_adding_closed_handle(self):
+ def check_adding_closed_handle(self, close_fn):
# init
m = pycurl.CurlMulti()
m.handles = []
@@ -157,17 +157,7 @@ class MultiTest(unittest.TestCase):
assert c.debug
c.http_code = -1
# pycurl API calls
- if 0:
- m.remove_handle(c)
- c.close()
- elif 0:
- # in the C API this is the wrong calling order, but pycurl
- # handles this automatically
- c.close()
- m.remove_handle(c)
- else:
- # actually, remove_handle is called automatically on close
- c.close()
+ close_fn(m, c)
m.close()
# check result
@@ -179,3 +169,26 @@ class MultiTest(unittest.TestCase):
# bottle generated response body
self.assertEqual('', m.handles[2].body.getvalue())
self.assertEqual(-1, m.handles[2].http_code)
+
+ def _remove_then_close(self, m, c):
+ m.remove_handle(c)
+ c.close()
+
+ def _close_then_remove(self, m, c):
+ # in the C API this is the wrong calling order, but pycurl
+ # handles this automatically
+ c.close()
+ m.remove_handle(c)
+
+ def _close_without_removing(self, m, c):
+ # actually, remove_handle is called automatically on close
+ c.close
+
+ def test_adding_closed_handle_remove_then_close(self):
+ self.check_adding_closed_handle(self._remove_then_close)
+
+ def test_adding_closed_handle_close_then_remove(self):
+ self.check_adding_closed_handle(self._close_then_remove)
+
+ def test_adding_closed_handle_close_without_removing(self):
+ self.check_adding_closed_handle(self._close_without_removing)
--
1.7.1
From 587c4b3403bf16ce5ab8a72e738ac26dab4a31c8 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Sun, 3 Mar 2013 14:21:33 -0500
Subject: [PATCH 038/149] Port mulit4 test
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/multi_test.py | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 50 insertions(+), 0 deletions(-)
diff --git a/tests/multi_test.py b/tests/multi_test.py
index e1fec05..a508809 100644
--- a/tests/multi_test.py
+++ b/tests/multi_test.py
@@ -4,6 +4,7 @@
import pycurl
import unittest
+import select
from . import app
from . import runwsgi
@@ -58,6 +59,55 @@ class MultiTest(unittest.TestCase):
self.assertEqual('success', io1.getvalue())
self.assertEqual('success', io2.getvalue())
+ def test_multi_select_fdset(self):
+ c1 = pycurl.Curl()
+ c2 = pycurl.Curl()
+ c3 = pycurl.Curl()
+ c1.setopt(c1.URL, "http://localhost:8380/success")
+ c2.setopt(c2.URL, "http://localhost:8381/success")
+ c3.setopt(c3.URL, "http://localhost:8382/success")
+ c1.body = util.StringIO()
+ c2.body = util.StringIO()
+ c3.body = util.StringIO()
+ c1.setopt(c1.WRITEFUNCTION, c1.body.write)
+ c2.setopt(c2.WRITEFUNCTION, c2.body.write)
+ c3.setopt(c3.WRITEFUNCTION, c3.body.write)
+
+ m = pycurl.CurlMulti()
+ m.add_handle(c1)
+ m.add_handle(c2)
+ m.add_handle(c3)
+
+ # Number of seconds to wait for a timeout to happen
+ SELECT_TIMEOUT = 0.1
+
+ # Stir the state machine into action
+ while 1:
+ ret, num_handles = m.perform()
+ if ret != pycurl.E_CALL_MULTI_PERFORM:
+ break
+
+ # Keep going until all the connections have terminated
+ while num_handles:
+ select.select(*m.fdset() + (SELECT_TIMEOUT,))
+ while 1:
+ ret, num_handles = m.perform()
+ if ret != pycurl.E_CALL_MULTI_PERFORM:
+ break
+
+ # Cleanup
+ m.remove_handle(c3)
+ m.remove_handle(c2)
+ m.remove_handle(c1)
+ m.close()
+ c1.close()
+ c2.close()
+ c3.close()
+
+ self.assertEqual('success', c1.body.getvalue())
+ self.assertEqual('success', c2.body.getvalue())
+ self.assertEqual('success', c3.body.getvalue())
+
def test_multi_status_codes(self):
# init
m = pycurl.CurlMulti()
--
1.7.1
From cd0e96615c24173e38177395931899aaa566c270 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Tue, 5 Mar 2013 00:24:44 -0500
Subject: [PATCH 039/149] Port multit6 test
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/multi_test.py | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 55 insertions(+), 0 deletions(-)
diff --git a/tests/multi_test.py b/tests/multi_test.py
index a508809..e83ce48 100644
--- a/tests/multi_test.py
+++ b/tests/multi_test.py
@@ -242,3 +242,58 @@ class MultiTest(unittest.TestCase):
def test_adding_closed_handle_close_without_removing(self):
self.check_adding_closed_handle(self._close_without_removing)
+
+ def test_multi_info_read(self):
+ c1 = pycurl.Curl()
+ c2 = pycurl.Curl()
+ c3 = pycurl.Curl()
+ c1.setopt(c1.URL, "http://localhost:8380/success")
+ c2.setopt(c2.URL, "http://localhost:8381/success")
+ c3.setopt(c3.URL, "http://localhost:8382/success")
+ c1.body = util.StringIO()
+ c2.body = util.StringIO()
+ c3.body = util.StringIO()
+ c1.setopt(c1.WRITEFUNCTION, c1.body.write)
+ c2.setopt(c2.WRITEFUNCTION, c2.body.write)
+ c3.setopt(c3.WRITEFUNCTION, c3.body.write)
+
+ m = pycurl.CurlMulti()
+ m.add_handle(c1)
+ m.add_handle(c2)
+ m.add_handle(c3)
+
+ # Number of seconds to wait for a timeout to happen
+ SELECT_TIMEOUT = 1.0
+
+ # Stir the state machine into action
+ while 1:
+ ret, num_handles = m.perform()
+ if ret != pycurl.E_CALL_MULTI_PERFORM:
+ break
+
+ # Keep going until all the connections have terminated
+ while num_handles:
+ # The select method uses fdset internally to determine which file descriptors
+ # to check.
+ m.select(SELECT_TIMEOUT)
+ while 1:
+ ret, num_handles = m.perform()
+ # Print the message, if any
+ while True:
+ info = m.info_read()
+ print info
+ if ret != pycurl.E_CALL_MULTI_PERFORM:
+ break
+
+ # Cleanup
+ m.remove_handle(c3)
+ m.remove_handle(c2)
+ m.remove_handle(c1)
+ m.close()
+ c1.close()
+ c2.close()
+ c3.close()
+
+ self.assertEqual('success', c1.body.getvalue())
+ self.assertEqual('success', c2.body.getvalue())
+ self.assertEqual('success', c3.body.getvalue())
--
1.7.1
From d59cc0dffc1f70545317c03307211559afe46b6d Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Tue, 5 Mar 2013 00:38:59 -0500
Subject: [PATCH 040/149] Improve info_read test to test info_read
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/multi_test.py | 19 +++++++++++++++----
1 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/tests/multi_test.py b/tests/multi_test.py
index e83ce48..10af44c 100644
--- a/tests/multi_test.py
+++ b/tests/multi_test.py
@@ -271,6 +271,7 @@ class MultiTest(unittest.TestCase):
if ret != pycurl.E_CALL_MULTI_PERFORM:
break
+ infos = []
# Keep going until all the connections have terminated
while num_handles:
# The select method uses fdset internally to determine which file descriptors
@@ -278,13 +279,23 @@ class MultiTest(unittest.TestCase):
m.select(SELECT_TIMEOUT)
while 1:
ret, num_handles = m.perform()
- # Print the message, if any
- while True:
- info = m.info_read()
- print info
+ info = m.info_read()
+ infos.append(info)
if ret != pycurl.E_CALL_MULTI_PERFORM:
break
+ all_handles = []
+ for info in infos:
+ handles = info[1]
+ # last info is an empty array
+ if handles:
+ all_handles.extend(handles)
+
+ self.assertEqual(3, len(all_handles))
+ assert c1 in all_handles
+ assert c2 in all_handles
+ assert c3 in all_handles
+
# Cleanup
m.remove_handle(c3)
m.remove_handle(c2)
--
1.7.1
From 9b239643fd425e600b467f91ce7ea0560dd88b79 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Tue, 5 Mar 2013 00:40:25 -0500
Subject: [PATCH 041/149] Port multi5 test
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/multi_test.py | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 51 insertions(+), 0 deletions(-)
diff --git a/tests/multi_test.py b/tests/multi_test.py
index 10af44c..d9c6174 100644
--- a/tests/multi_test.py
+++ b/tests/multi_test.py
@@ -243,6 +243,57 @@ class MultiTest(unittest.TestCase):
def test_adding_closed_handle_close_without_removing(self):
self.check_adding_closed_handle(self._close_without_removing)
+ def test_multi_select(self):
+ c1 = pycurl.Curl()
+ c2 = pycurl.Curl()
+ c3 = pycurl.Curl()
+ c1.setopt(c1.URL, "http://localhost:8380/success")
+ c2.setopt(c2.URL, "http://localhost:8381/success")
+ c3.setopt(c3.URL, "http://localhost:8382/success")
+ c1.body = util.StringIO()
+ c2.body = util.StringIO()
+ c3.body = util.StringIO()
+ c1.setopt(c1.WRITEFUNCTION, c1.body.write)
+ c2.setopt(c2.WRITEFUNCTION, c2.body.write)
+ c3.setopt(c3.WRITEFUNCTION, c3.body.write)
+
+ m = pycurl.CurlMulti()
+ m.add_handle(c1)
+ m.add_handle(c2)
+ m.add_handle(c3)
+
+ # Number of seconds to wait for a timeout to happen
+ SELECT_TIMEOUT = 1.0
+
+ # Stir the state machine into action
+ while 1:
+ ret, num_handles = m.perform()
+ if ret != pycurl.E_CALL_MULTI_PERFORM:
+ break
+
+ # Keep going until all the connections have terminated
+ while num_handles:
+ # The select method uses fdset internally to determine which file descriptors
+ # to check.
+ m.select(SELECT_TIMEOUT)
+ while 1:
+ ret, num_handles = m.perform()
+ if ret != pycurl.E_CALL_MULTI_PERFORM:
+ break
+
+ # Cleanup
+ m.remove_handle(c3)
+ m.remove_handle(c2)
+ m.remove_handle(c1)
+ m.close()
+ c1.close()
+ c2.close()
+ c3.close()
+
+ self.assertEqual('success', c1.body.getvalue())
+ self.assertEqual('success', c2.body.getvalue())
+ self.assertEqual('success', c3.body.getvalue())
+
def test_multi_info_read(self):
c1 = pycurl.Curl()
c2 = pycurl.Curl()
--
1.7.1
From c16f8df53e9f97d2ef4c0acb19e4abcda5825260 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Tue, 5 Mar 2013 22:05:34 -0500
Subject: [PATCH 042/149] Ported test_multi_socket.py
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/multi_socket_test.py | 110 ++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 110 insertions(+), 0 deletions(-)
create mode 100644 tests/multi_socket_test.py
diff --git a/tests/multi_socket_test.py b/tests/multi_socket_test.py
new file mode 100644
index 0000000..2586569
--- /dev/null
+++ b/tests/multi_socket_test.py
@@ -0,0 +1,110 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+import pycurl
+import unittest
+import select
+
+from . import app
+from . import runwsgi
+from . import util
+
+setup_module_1, teardown_module_1 = runwsgi.app_runner_setup((app.app, 8380))
+setup_module_2, teardown_module_2 = runwsgi.app_runner_setup((app.app, 8381))
+setup_module_3, teardown_module_3 = runwsgi.app_runner_setup((app.app, 8382))
+
+def setup_module(mod):
+ setup_module_1(mod)
+ setup_module_2(mod)
+ setup_module_3(mod)
+
+def teardown_module(mod):
+ teardown_module_3(mod)
+ teardown_module_2(mod)
+ teardown_module_1(mod)
+
+class MultiSocketTest(unittest.TestCase):
+ def test_multi_socket(self):
+ urls = [
+ 'http://localhost:8380/success',
+ 'http://localhost:8381/success',
+ 'http://localhost:8382/success',
+ ]
+
+ timers = []
+
+ # timer callback
+ def timer(msecs):
+ #print('Timer callback msecs:', msecs)
+ timers.append(msecs)
+
+ socket_events = []
+
+ # socket callback
+ def socket(event, socket, multi, data):
+ #print(event, socket, multi, data)
+ # multi.assign(socket, timer)
+ socket_events.append((event, multi))
+
+ # init
+ m = pycurl.CurlMulti()
+ m.setopt(pycurl.M_PIPELINING, 1)
+ m.setopt(pycurl.M_TIMERFUNCTION, timer)
+ m.setopt(pycurl.M_SOCKETFUNCTION, socket)
+ m.handles = []
+ for url in urls:
+ c = pycurl.Curl()
+ # save info in standard Python attributes
+ c.url = url
+ c.body = util.StringIO()
+ c.http_code = -1
+ m.handles.append(c)
+ # pycurl API calls
+ c.setopt(c.URL, c.url)
+ c.setopt(c.WRITEFUNCTION, c.body.write)
+ m.add_handle(c)
+
+ # get data
+ num_handles = len(m.handles)
+ while num_handles:
+ while 1:
+ ret, num_handles = m.socket_all()
+ if ret != pycurl.E_CALL_MULTI_PERFORM:
+ break
+ # currently no more I/O is pending, could do something in the meantime
+ # (display a progress bar, etc.)
+ m.select(0.1)
+
+ for c in m.handles:
+ # save info in standard Python attributes
+ c.http_code = c.getinfo(c.HTTP_CODE)
+
+ # at least in and remove events per socket
+ assert len(socket_events) >= 6
+
+ # print result
+ for c in m.handles:
+ self.assertEqual('success', c.body.getvalue())
+ self.assertEqual(200, c.http_code)
+
+ # multi, not curl handle
+ self.check(pycurl.POLL_IN, m, socket_events)
+ self.check(pycurl.POLL_REMOVE, m, socket_events)
+
+ assert len(timers) > 0
+ assert timers[0] > 0
+ self.assertEqual(-1, timers[-1])
+
+ # close handles
+ for c in m.handles:
+ # pycurl API calls
+ m.remove_handle(c)
+ c.close()
+ m.close()
+
+ def check(self, event, multi, socket_events):
+ for event_, multi_ in socket_events:
+ if event == event_ and multi == multi_:
+ return
+ assert False, '%d %s not found in socket events' % (event, multi)
--
1.7.1
From 9a0ae278c27b16fa7dc745c2add39101a13fc9ec Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Tue, 5 Mar 2013 22:29:45 -0500
Subject: [PATCH 043/149] Ported test_multi_socket_select.py
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/multi_socket_select_test.py | 130 +++++++++++++++++++++++++++++++++++++
1 files changed, 130 insertions(+), 0 deletions(-)
create mode 100644 tests/multi_socket_select_test.py
diff --git a/tests/multi_socket_select_test.py b/tests/multi_socket_select_test.py
new file mode 100644
index 0000000..b9ba950
--- /dev/null
+++ b/tests/multi_socket_select_test.py
@@ -0,0 +1,130 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+import pycurl
+import unittest
+import select
+
+from . import app
+from . import runwsgi
+from . import util
+
+setup_module_1, teardown_module_1 = runwsgi.app_runner_setup((app.app, 8380))
+setup_module_2, teardown_module_2 = runwsgi.app_runner_setup((app.app, 8381))
+setup_module_3, teardown_module_3 = runwsgi.app_runner_setup((app.app, 8382))
+
+def setup_module(mod):
+ setup_module_1(mod)
+ setup_module_2(mod)
+ setup_module_3(mod)
+
+def teardown_module(mod):
+ teardown_module_3(mod)
+ teardown_module_2(mod)
+ teardown_module_1(mod)
+
+class MultiSocketSelectTest(unittest.TestCase):
+ def test_multi_socket_select(self):
+ sockets = set()
+ timeout = 0
+
+ urls = [
+ 'http://localhost:8380/success',
+ 'http://localhost:8381/success',
+ 'http://localhost:8382/success',
+ ]
+
+ timers = []
+
+ # timer callback
+ def timer(msecs):
+ #print('Timer callback msecs:', msecs)
+ timers.append(msecs)
+
+ socket_events = []
+
+ # socket callback
+ def socket(event, socket, multi, data):
+ if event == pycurl.POLL_REMOVE:
+ #print("Remove Socket %d"%socket)
+ sockets.remove(socket)
+ else:
+ if socket not in sockets:
+ #print("Add socket %d"%socket)
+ sockets.add(socket)
+ socket_events.append((event, multi))
+
+ # init
+ m = pycurl.CurlMulti()
+ m.setopt(pycurl.M_PIPELINING, 1)
+ m.setopt(pycurl.M_TIMERFUNCTION, timer)
+ m.setopt(pycurl.M_SOCKETFUNCTION, socket)
+ m.handles = []
+ for url in urls:
+ c = pycurl.Curl()
+ # save info in standard Python attributes
+ c.url = url
+ c.body = util.StringIO()
+ c.http_code = -1
+ m.handles.append(c)
+ # pycurl API calls
+ c.setopt(c.URL, c.url)
+ c.setopt(c.WRITEFUNCTION, c.body.write)
+ m.add_handle(c)
+
+ # get data
+ num_handles = len(m.handles)
+
+ while (pycurl.E_CALL_MULTI_PERFORM==m.socket_all()[0]):
+ pass
+
+ timeout = m.timeout()
+
+
+ while True:
+ (rr, wr, er) = select.select(sockets,sockets,sockets,timeout/1000.0)
+ socketSet = set(rr+wr+er)
+ if socketSet:
+ for s in socketSet:
+ while True:
+ (ret,running) = m.socket_action(s,0)
+ if ret!=pycurl.E_CALL_MULTI_PERFORM:
+ break
+ else:
+ (ret,running) = m.socket_action(pycurl.SOCKET_TIMEOUT,0)
+ if running==0:
+ break
+
+ for c in m.handles:
+ # save info in standard Python attributes
+ c.http_code = c.getinfo(c.HTTP_CODE)
+
+ # at least in and remove events per socket
+ assert len(socket_events) >= 6
+
+ # print result
+ for c in m.handles:
+ self.assertEqual('success', c.body.getvalue())
+ self.assertEqual(200, c.http_code)
+
+ # multi, not curl handle
+ self.check(pycurl.POLL_IN, m, socket_events)
+ self.check(pycurl.POLL_REMOVE, m, socket_events)
+
+ assert len(timers) > 0
+ assert timers[0] > 0
+ self.assertEqual(-1, timers[-1])
+
+ # close handles
+ for c in m.handles:
+ # pycurl API calls
+ m.remove_handle(c)
+ c.close()
+ m.close()
+
+ def check(self, event, multi, socket_events):
+ for event_, multi_ in socket_events:
+ if event == event_ and multi == multi_:
+ return
+ assert False, '%d %s not found in socket events' % (event, multi)
--
1.7.1
From 7631c7c047354c1dacb09459fefd6ea9ca11a28b Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Tue, 5 Mar 2013 23:15:47 -0500
Subject: [PATCH 044/149] Ported test_multi_timer.py
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/multi_timer_test.py | 88 +++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 88 insertions(+), 0 deletions(-)
create mode 100644 tests/multi_timer_test.py
diff --git a/tests/multi_timer_test.py b/tests/multi_timer_test.py
new file mode 100644
index 0000000..e961780
--- /dev/null
+++ b/tests/multi_timer_test.py
@@ -0,0 +1,88 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+import pycurl
+import unittest
+import select
+
+from . import app
+from . import runwsgi
+from . import util
+
+setup_module_1, teardown_module_1 = runwsgi.app_runner_setup((app.app, 8380))
+setup_module_2, teardown_module_2 = runwsgi.app_runner_setup((app.app, 8381))
+setup_module_3, teardown_module_3 = runwsgi.app_runner_setup((app.app, 8382))
+
+def setup_module(mod):
+ setup_module_1(mod)
+ setup_module_2(mod)
+ setup_module_3(mod)
+
+def teardown_module(mod):
+ teardown_module_3(mod)
+ teardown_module_2(mod)
+ teardown_module_1(mod)
+
+class MultiSocketTest(unittest.TestCase):
+ def test_multi_timer(self):
+ urls = [
+ 'http://localhost:8380/success',
+ 'http://localhost:8381/success',
+ 'http://localhost:8382/success',
+ ]
+
+ timers = []
+
+ # timer callback
+ def timer(msecs):
+ #print('Timer callback msecs:', msecs)
+ timers.append(msecs)
+
+ # init
+ m = pycurl.CurlMulti()
+ m.setopt(pycurl.M_PIPELINING, 1)
+ m.setopt(pycurl.M_TIMERFUNCTION, timer)
+ m.handles = []
+ for url in urls:
+ c = pycurl.Curl()
+ # save info in standard Python attributes
+ c.url = url
+ c.body = util.StringIO()
+ c.http_code = -1
+ m.handles.append(c)
+ # pycurl API calls
+ c.setopt(c.URL, c.url)
+ c.setopt(c.WRITEFUNCTION, c.body.write)
+ m.add_handle(c)
+
+ # get data
+ num_handles = len(m.handles)
+ while num_handles:
+ while 1:
+ ret, num_handles = m.perform()
+ if ret != pycurl.E_CALL_MULTI_PERFORM:
+ break
+ # currently no more I/O is pending, could do something in the meantime
+ # (display a progress bar, etc.)
+ m.select(1.0)
+
+ for c in m.handles:
+ # save info in standard Python attributes
+ c.http_code = c.getinfo(c.HTTP_CODE)
+
+ # print result
+ for c in m.handles:
+ self.assertEqual('success', c.body.getvalue())
+ self.assertEqual(200, c.http_code)
+
+ assert len(timers) > 0
+ assert timers[0] > 0
+ self.assertEqual(-1, timers[-1])
+
+ # close handles
+ for c in m.handles:
+ # pycurl API calls
+ m.remove_handle(c)
+ c.close()
+ m.close()
--
1.7.1
From eb523b0b4e3729d6613e1841d3d3715ad6e58109 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Tue, 5 Mar 2013 23:17:14 -0500
Subject: [PATCH 045/149] Timers are tested in multi_timer test, delete them from other multi tests
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/multi_socket_select_test.py | 12 ------------
tests/multi_socket_test.py | 13 -------------
2 files changed, 0 insertions(+), 25 deletions(-)
diff --git a/tests/multi_socket_select_test.py b/tests/multi_socket_select_test.py
index b9ba950..0c472cf 100644
--- a/tests/multi_socket_select_test.py
+++ b/tests/multi_socket_select_test.py
@@ -35,13 +35,6 @@ class MultiSocketSelectTest(unittest.TestCase):
'http://localhost:8382/success',
]
- timers = []
-
- # timer callback
- def timer(msecs):
- #print('Timer callback msecs:', msecs)
- timers.append(msecs)
-
socket_events = []
# socket callback
@@ -58,7 +51,6 @@ class MultiSocketSelectTest(unittest.TestCase):
# init
m = pycurl.CurlMulti()
m.setopt(pycurl.M_PIPELINING, 1)
- m.setopt(pycurl.M_TIMERFUNCTION, timer)
m.setopt(pycurl.M_SOCKETFUNCTION, socket)
m.handles = []
for url in urls:
@@ -112,10 +104,6 @@ class MultiSocketSelectTest(unittest.TestCase):
self.check(pycurl.POLL_IN, m, socket_events)
self.check(pycurl.POLL_REMOVE, m, socket_events)
- assert len(timers) > 0
- assert timers[0] > 0
- self.assertEqual(-1, timers[-1])
-
# close handles
for c in m.handles:
# pycurl API calls
diff --git a/tests/multi_socket_test.py b/tests/multi_socket_test.py
index 2586569..2cce7ae 100644
--- a/tests/multi_socket_test.py
+++ b/tests/multi_socket_test.py
@@ -32,25 +32,16 @@ class MultiSocketTest(unittest.TestCase):
'http://localhost:8382/success',
]
- timers = []
-
- # timer callback
- def timer(msecs):
- #print('Timer callback msecs:', msecs)
- timers.append(msecs)
-
socket_events = []
# socket callback
def socket(event, socket, multi, data):
#print(event, socket, multi, data)
- # multi.assign(socket, timer)
socket_events.append((event, multi))
# init
m = pycurl.CurlMulti()
m.setopt(pycurl.M_PIPELINING, 1)
- m.setopt(pycurl.M_TIMERFUNCTION, timer)
m.setopt(pycurl.M_SOCKETFUNCTION, socket)
m.handles = []
for url in urls:
@@ -92,10 +83,6 @@ class MultiSocketTest(unittest.TestCase):
self.check(pycurl.POLL_IN, m, socket_events)
self.check(pycurl.POLL_REMOVE, m, socket_events)
- assert len(timers) > 0
- assert timers[0] > 0
- self.assertEqual(-1, timers[-1])
-
# close handles
for c in m.handles:
# pycurl API calls
--
1.7.1
From c4ad7c4b2cf2107fb4dfcf468fef554bb8f1a38f Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Tue, 5 Mar 2013 23:30:48 -0500
Subject: [PATCH 046/149] Ported post test
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/app.py | 5 +++++
tests/post_test.py | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 54 insertions(+), 0 deletions(-)
create mode 100644 tests/post_test.py
diff --git a/tests/app.py b/tests/app.py
index 3b9303b..1fa09a6 100644
--- a/tests/app.py
+++ b/tests/app.py
@@ -1,4 +1,5 @@
import bottle
+import json
app = bottle.Bottle()
@@ -13,3 +14,7 @@ def forbidden():
@app.route('/status/404')
def not_found():
bottle.abort(404, 'not found')
+
+@app.route('/postfields', method='post')
+def postfields():
+ return json.dumps(dict(bottle.request.forms))
diff --git a/tests/post_test.py b/tests/post_test.py
new file mode 100644
index 0000000..183c4c9
--- /dev/null
+++ b/tests/post_test.py
@@ -0,0 +1,49 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+import pycurl
+import unittest
+import io
+import json
+try:
+ import urllib.parse as urllib_parse
+except ImportError:
+ import urllib as urllib_parse
+
+from . import app
+from . import runwsgi
+from . import util
+
+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380))
+
+class PostTest(unittest.TestCase):
+ def setUp(self):
+ self.curl = pycurl.Curl()
+
+ def tearDown(self):
+ self.curl.close()
+
+ def test_post_single_field(self):
+ pf = {'field1': 'value1'}
+ self.check(pf)
+
+ def test_post_multiple_fields(self):
+ pf = {'field1':'value1', 'field2':'value2 with blanks', 'field3':'value3'}
+ self.check(pf)
+
+ def test_post_fields_with_ampersand(self):
+ pf = {'field1':'value1', 'field2':'value2 with blanks and & chars',
+ 'field3':'value3'}
+ self.check(pf)
+
+ def check(self, pf):
+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/postfields')
+ self.curl.setopt(pycurl.POSTFIELDS, urllib_parse.urlencode(pf))
+ #self.curl.setopt(pycurl.VERBOSE, 1)
+ sio = util.StringIO()
+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
+ self.curl.perform()
+ body = sio.getvalue()
+ returned_fields = json.loads(body)
+ self.assertEqual(pf, returned_fields)
--
1.7.1
From 789b647eff13181fa527cb218dd487695abf3515 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Tue, 5 Mar 2013 23:35:02 -0500
Subject: [PATCH 047/149] Port test_post2.py null byte test
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/post_test.py | 28 ++++++++++++++++++++++++----
1 files changed, 24 insertions(+), 4 deletions(-)
diff --git a/tests/post_test.py b/tests/post_test.py
index 183c4c9..6f9cf80 100644
--- a/tests/post_test.py
+++ b/tests/post_test.py
@@ -26,18 +26,18 @@ class PostTest(unittest.TestCase):
def test_post_single_field(self):
pf = {'field1': 'value1'}
- self.check(pf)
+ self.urlencode_and_check(pf)
def test_post_multiple_fields(self):
pf = {'field1':'value1', 'field2':'value2 with blanks', 'field3':'value3'}
- self.check(pf)
+ self.urlencode_and_check(pf)
def test_post_fields_with_ampersand(self):
pf = {'field1':'value1', 'field2':'value2 with blanks and & chars',
'field3':'value3'}
- self.check(pf)
+ self.urlencode_and_check(pf)
- def check(self, pf):
+ def urlencode_and_check(self, pf):
self.curl.setopt(pycurl.URL, 'http://localhost:8380/postfields')
self.curl.setopt(pycurl.POSTFIELDS, urllib_parse.urlencode(pf))
#self.curl.setopt(pycurl.VERBOSE, 1)
@@ -47,3 +47,23 @@ class PostTest(unittest.TestCase):
body = sio.getvalue()
returned_fields = json.loads(body)
self.assertEqual(pf, returned_fields)
+
+ def test_post_with_null_byte(self):
+ send = [
+ ('field3', (pycurl.FORM_CONTENTS, 'this is wei\000rd, but null-bytes are okay'))
+ ]
+ expect = {
+ 'field3': 'this is wei\000rd, but null-bytes are okay',
+ }
+ self.check_post(send, expect)
+
+ def check_post(self, send, expect):
+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/postfields')
+ self.curl.setopt(pycurl.HTTPPOST, send)
+ #self.curl.setopt(pycurl.VERBOSE, 1)
+ sio = util.StringIO()
+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
+ self.curl.perform()
+ body = sio.getvalue()
+ returned_fields = json.loads(body)
+ self.assertEqual(expect, returned_fields)
--
1.7.1
From 2f56e4844e78280410f781b04d43ac49ce08129e Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Tue, 5 Mar 2013 23:50:51 -0500
Subject: [PATCH 048/149] Port test_post2.py file upload test
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/app.py | 24 ++++++++++++++++++++++++
tests/post_test.py | 22 +++++++++++++++++++---
2 files changed, 43 insertions(+), 3 deletions(-)
diff --git a/tests/app.py b/tests/app.py
index 1fa09a6..a83e628 100644
--- a/tests/app.py
+++ b/tests/app.py
@@ -18,3 +18,27 @@ def not_found():
@app.route('/postfields', method='post')
def postfields():
return json.dumps(dict(bottle.request.forms))
+
+# XXX file is not a bottle FileUpload instance, but FieldStorage?
+def convert_file(key, file):
+ return {
+ 'key': key,
+ 'name': file.name,
+ 'raw_filename': file.raw_filename,
+ 'headers': file.headers,
+ 'content_type': file.content_type,
+ 'content_length': file.content_length,
+ 'data': file.read(),
+ }
+
+def convert_file(key, file):
+ return {
+ 'name': file.name,
+ 'filename': file.filename,
+ 'data': file.file.read(),
+ }
+
+@app.route('/files', method='post')
+def files():
+ files = [convert_file(key, bottle.request.files[key]) for key in bottle.request.files]
+ return json.dumps(files)
diff --git a/tests/post_test.py b/tests/post_test.py
index 6f9cf80..7df0f3b 100644
--- a/tests/post_test.py
+++ b/tests/post_test.py
@@ -2,6 +2,7 @@
# -*- coding: iso-8859-1 -*-
# vi:ts=4:et
+import os.path
import pycurl
import unittest
import io
@@ -55,10 +56,25 @@ class PostTest(unittest.TestCase):
expect = {
'field3': 'this is wei\000rd, but null-bytes are okay',
}
- self.check_post(send, expect)
+ self.check_post(send, expect, 'http://localhost:8380/postfields')
- def check_post(self, send, expect):
- self.curl.setopt(pycurl.URL, 'http://localhost:8380/postfields')
+ def test_post_file(self):
+ path = os.path.join(os.path.dirname(__file__), '..', 'README')
+ with open(path) as f:
+ contents = f.read()
+ send = [
+ #('field2', (pycurl.FORM_FILE, 'test_post.py', pycurl.FORM_FILE, 'test_post2.py')),
+ ('field2', (pycurl.FORM_FILE, path)),
+ ]
+ expect = [{
+ 'name': 'field2',
+ 'filename': 'README',
+ 'data': contents,
+ }]
+ self.check_post(send, expect, 'http://localhost:8380/files')
+
+ def check_post(self, send, expect, endpoint):
+ self.curl.setopt(pycurl.URL, endpoint)
self.curl.setopt(pycurl.HTTPPOST, send)
#self.curl.setopt(pycurl.VERBOSE, 1)
sio = util.StringIO()
--
1.7.1
From c8f91c64ad1eb067907e9b97bd5f8bfa27c29062 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Tue, 5 Mar 2013 23:51:45 -0500
Subject: [PATCH 049/149] Add a note that this test takes forever to run
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/post_test.py | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/tests/post_test.py b/tests/post_test.py
index 7df0f3b..564d043 100644
--- a/tests/post_test.py
+++ b/tests/post_test.py
@@ -73,6 +73,7 @@ class PostTest(unittest.TestCase):
}]
self.check_post(send, expect, 'http://localhost:8380/files')
+ # XXX this test takes about a second to run, check keep-alives?
def check_post(self, send, expect, endpoint):
self.curl.setopt(pycurl.URL, endpoint)
self.curl.setopt(pycurl.HTTPPOST, send)
--
1.7.1
From 9ac9dce97226d34e6bb077a0357063fd23056158 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Tue, 5 Mar 2013 23:59:04 -0500
Subject: [PATCH 050/149] Ported test_post3.py
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/post_with_read_callback_test.py | 60 +++++++++++++++++++++++++++++++++
1 files changed, 60 insertions(+), 0 deletions(-)
create mode 100644 tests/post_with_read_callback_test.py
diff --git a/tests/post_with_read_callback_test.py b/tests/post_with_read_callback_test.py
new file mode 100644
index 0000000..a09e83a
--- /dev/null
+++ b/tests/post_with_read_callback_test.py
@@ -0,0 +1,60 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+import os.path
+import pycurl
+import unittest
+import io
+import json
+try:
+ import urllib.parse as urllib_parse
+except ImportError:
+ import urllib as urllib_parse
+
+from . import app
+from . import runwsgi
+from . import util
+
+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380))
+
+POSTFIELDS = {
+ 'field1':'value1',
+ 'field2':'value2 with blanks',
+ 'field3':'value3',
+}
+POSTSTRING = urllib_parse.urlencode(POSTFIELDS)
+
+class DataProvider(object):
+ def __init__(self):
+ self.finished = False
+
+ def read_cb(self, size):
+ assert len(POSTSTRING) <= size
+ if not self.finished:
+ self.finished = True
+ return POSTSTRING
+ else:
+ # Nothing more to read
+ return ""
+
+class PostWithReadCallbackTest(unittest.TestCase):
+ def setUp(self):
+ self.curl = pycurl.Curl()
+
+ def tearDown(self):
+ self.curl.close()
+
+ def test_post_with_read_callback(self):
+ d = DataProvider()
+ self.curl.setopt(self.curl.URL, 'http://localhost:8380/postfields')
+ self.curl.setopt(self.curl.POST, 1)
+ self.curl.setopt(self.curl.POSTFIELDSIZE, len(POSTSTRING))
+ self.curl.setopt(self.curl.READFUNCTION, d.read_cb)
+ #self.curl.setopt(self.curl.VERBOSE, 1)
+ sio = util.StringIO()
+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
+ self.curl.perform()
+
+ actual = json.loads(sio.getvalue())
+ self.assertEqual(POSTFIELDS, actual)
--
1.7.1
From 8a4babacffc0bbeacdfcfb3f9888f26efd6c072c Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 00:10:41 -0500
Subject: [PATCH 051/149] Ported test_socketopen.py
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/socket_open_test.py | 45 +++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 45 insertions(+), 0 deletions(-)
create mode 100644 tests/socket_open_test.py
diff --git a/tests/socket_open_test.py b/tests/socket_open_test.py
new file mode 100644
index 0000000..dff3f65
--- /dev/null
+++ b/tests/socket_open_test.py
@@ -0,0 +1,45 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+import socket
+import pycurl
+import unittest
+try:
+ import urllib.parse as urllib_parse
+except ImportError:
+ import urllib as urllib_parse
+
+from . import app
+from . import runwsgi
+from . import util
+
+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380))
+
+socket_open_called = False
+
+def socket_open(family, socktype, protocol):
+ global socket_open_called
+ socket_open_called = True
+
+ #print(family, socktype, protocol)
+ s = socket.socket(family, socktype, protocol)
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
+ return s
+
+class SocketOpenTest(unittest.TestCase):
+ def setUp(self):
+ self.curl = pycurl.Curl()
+
+ def tearDown(self):
+ self.curl.close()
+
+ def test_socket_open(self):
+ self.curl.setopt(pycurl.OPENSOCKETFUNCTION, socket_open)
+ self.curl.setopt(self.curl.URL, 'http://localhost:8380/success')
+ sio = util.StringIO()
+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
+ self.curl.perform()
+
+ assert socket_open_called
+ self.assertEqual('success', sio.getvalue())
--
1.7.1
From bf65a3bac3446950421927da8230216acefebf86 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 00:13:46 -0500
Subject: [PATCH 052/149] Ported test_share.py
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/share_test.py | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 51 insertions(+), 0 deletions(-)
create mode 100644 tests/share_test.py
diff --git a/tests/share_test.py b/tests/share_test.py
new file mode 100644
index 0000000..7b5da77
--- /dev/null
+++ b/tests/share_test.py
@@ -0,0 +1,51 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+import threading
+import pycurl
+import unittest
+try:
+ import urllib.parse as urllib_parse
+except ImportError:
+ import urllib as urllib_parse
+
+from . import app
+from . import runwsgi
+from . import util
+
+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380))
+
+class WorkerThread(threading.Thread):
+
+ def __init__(self, share):
+ threading.Thread.__init__(self)
+ self.curl = pycurl.Curl()
+ self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
+ self.curl.setopt(pycurl.SHARE, share)
+ self.sio = util.StringIO()
+ self.curl.setopt(pycurl.WRITEFUNCTION, self.sio.write)
+
+ def run(self):
+ self.curl.perform()
+ self.curl.close()
+
+class ShareTest(unittest.TestCase):
+ def test_share(self):
+ s = pycurl.CurlShare()
+ s.setopt(pycurl.SH_SHARE, pycurl.LOCK_DATA_COOKIE)
+ s.setopt(pycurl.SH_SHARE, pycurl.LOCK_DATA_DNS)
+
+ t1 = WorkerThread(s)
+ t2 = WorkerThread(s)
+
+ t1.start()
+ t2.start()
+
+ t1.join()
+ t2.join()
+
+ del s
+
+ self.assertEqual('success', t1.sio.getvalue())
+ self.assertEqual('success', t2.sio.getvalue())
--
1.7.1
From c5f46d8247e3cbf3d2a0a2cf74c7daeade97c1d8 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 00:26:45 -0500
Subject: [PATCH 053/149] Port test_reset.py, which appears to be broken; skip the ported version for now
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/reset_test.py | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 76 insertions(+), 0 deletions(-)
create mode 100644 tests/reset_test.py
diff --git a/tests/reset_test.py b/tests/reset_test.py
new file mode 100644
index 0000000..66b7108
--- /dev/null
+++ b/tests/reset_test.py
@@ -0,0 +1,76 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+import sys
+import pycurl
+import unittest
+try:
+ import urllib.parse as urllib_parse
+except ImportError:
+ import urllib as urllib_parse
+
+from . import app
+from . import runwsgi
+from . import util
+
+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380))
+
+class ResetTest(unittest.TestCase):
+ # XXX this test was broken when it was test_reset.py
+ def skip_test_reset(self):
+ outf = util.StringIO()
+ cm = pycurl.CurlMulti()
+
+ # Set multi handle's options
+ cm.setopt(pycurl.M_PIPELINING, 1)
+
+ eh = pycurl.Curl()
+
+ for x in range(1, 20):
+
+ eh.setopt(pycurl.WRITEFUNCTION, outf.write)
+ eh.setopt(pycurl.URL, 'http://localhost:8380/success')
+ cm.add_handle(eh)
+
+ while 1:
+ ret, active_handles = cm.perform()
+ if ret != pycurl.E_CALL_MULTI_PERFORM:
+ break
+
+ while active_handles:
+ ret = cm.select(1.0)
+ if ret == -1:
+ continue
+ while 1:
+ ret, active_handles = cm.perform()
+ if ret != pycurl.E_CALL_MULTI_PERFORM:
+ break
+
+ count, good, bad = cm.info_read()
+
+ for h, en, em in bad:
+ print("Transfer to %s failed with %d, %s\n" % \
+ (h.getinfo(pycurl.EFFECTIVE_URL), en, em))
+ raise RuntimeError
+
+ for h in good:
+ httpcode = h.getinfo(pycurl.RESPONSE_CODE)
+ if httpcode != 200:
+ print("Transfer to %s failed with code %d\n" %\
+ (h.getinfo(pycurl.EFFECTIVE_URL), httpcode))
+ raise RuntimeError
+
+ else:
+ print("Recd %d bytes from %s" % \
+ (h.getinfo(pycurl.SIZE_DOWNLOAD),
+ h.getinfo(pycurl.EFFECTIVE_URL)))
+
+ cm.remove_handle(eh)
+ eh.reset()
+
+ eh.close()
+ cm.close()
+ outf.close()
+
+ pycurl.global_cleanup()
--
1.7.1
From 4aba306862de76d584af790bf2dd90f0d55fda0e Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 00:55:41 -0500
Subject: [PATCH 054/149] Start a server on each port once, different servers should go on different ports
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/runwsgi.py | 9 ++++++++-
1 files changed, 8 insertions(+), 1 deletions(-)
diff --git a/tests/runwsgi.py b/tests/runwsgi.py
index 7b39358..6d3b69f 100644
--- a/tests/runwsgi.py
+++ b/tests/runwsgi.py
@@ -48,6 +48,8 @@ class ServerThread(threading.Thread):
def run(self):
bottle.run(self.app, server=self.server, quiet=True)
+started_servers = {}
+
def app_runner_setup(*specs):
'''Returns setup and teardown methods for running a list of WSGI
applications in a daemon thread.
@@ -73,9 +75,14 @@ def app_runner_setup(*specs):
kwargs = {}
else:
app, port, kwargs = spec
- self.servers.append(start_bottle_server(app, port, **kwargs))
+ if port in started_servers:
+ assert started_servers[port] == (app, kwargs)
+ else:
+ self.servers.append(start_bottle_server(app, port, **kwargs))
+ started_servers[port] = (app, kwargs)
def teardown(self):
+ return
for server in self.servers:
# if no tests from module were run, there is no server to shut down
if hasattr(server, 'srv'):
--
1.7.1
From 8fc24d93e2188d68c738e602c75753cec503de7d Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 00:56:44 -0500
Subject: [PATCH 055/149] Need to skip more aggressively it would seem
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/default_write_function_test.py | 2 +-
tests/reset_test.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/default_write_function_test.py b/tests/default_write_function_test.py
index a0de66e..c0d256b 100644
--- a/tests/default_write_function_test.py
+++ b/tests/default_write_function_test.py
@@ -40,7 +40,7 @@ class DefaultWriteFunctionTest(unittest.TestCase):
os.fsync(STDOUT_FD_NUM)
# I have a really hard time getting this to work with nose output capture
- def skip_test_perform_get_with_default_write_function(self):
+ def skip_perform_get_with_default_write_function(self):
self.curl.setopt(pycurl.URL, 'http://localhost:8380/success')
with tempfile.NamedTemporaryFile() as f:
#with open('w', 'w+') as f:
diff --git a/tests/reset_test.py b/tests/reset_test.py
index 66b7108..cc55f86 100644
--- a/tests/reset_test.py
+++ b/tests/reset_test.py
@@ -18,7 +18,7 @@ setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380))
class ResetTest(unittest.TestCase):
# XXX this test was broken when it was test_reset.py
- def skip_test_reset(self):
+ def skip_reset(self):
outf = util.StringIO()
cm = pycurl.CurlMulti()
--
1.7.1
From d3a77eabe019b1707a7bdf8cbae32d1eddd12f9c Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 01:29:59 -0500
Subject: [PATCH 056/149] Store urlencode result in a local variable, otherwise things break in a highly weird way
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/post_test.py | 24 +++++++++++++++++++++++-
1 files changed, 23 insertions(+), 1 deletions(-)
diff --git a/tests/post_test.py b/tests/post_test.py
index 564d043..e8b0675 100644
--- a/tests/post_test.py
+++ b/tests/post_test.py
@@ -40,11 +40,33 @@ class PostTest(unittest.TestCase):
def urlencode_and_check(self, pf):
self.curl.setopt(pycurl.URL, 'http://localhost:8380/postfields')
- self.curl.setopt(pycurl.POSTFIELDS, urllib_parse.urlencode(pf))
+ postfields = urllib_parse.urlencode(pf)
+ self.curl.setopt(pycurl.POSTFIELDS, postfields)
+
+ # But directly passing urlencode result into setopt call:
+ #self.curl.setopt(pycurl.POSTFIELDS, urllib_parse.urlencode(pf))
+ # produces:
+ # {'\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00': ''}
+ # Traceback (most recent call last):
+ # File "/usr/local/bin/bottle.py", line 744, in _handle
+ # return route.call(**args)
+ # File "/usr/local/bin/bottle.py", line 1479, in wrapper
+ # rv = callback(*a, **ka)
+ # File "/home/pie/apps/pycurl/tests/app.py", line 21, in postfields
+ # return json.dumps(dict(bottle.request.forms))
+ # File "/usr/local/lib/python2.7/json/__init__.py", line 231, in dumps
+ # return _default_encoder.encode(obj)
+ # File "/usr/local/lib/python2.7/json/encoder.py", line 201, in encode
+ # chunks = self.iterencode(o, _one_shot=True)
+ # File "/usr/local/lib/python2.7/json/encoder.py", line 264, in iterencode
+ # return _iterencode(o, 0)
+ # UnicodeDecodeError: 'utf8' codec can't decode byte 0x80 in position 4: invalid start byte
+
#self.curl.setopt(pycurl.VERBOSE, 1)
sio = util.StringIO()
self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
self.curl.perform()
+ self.assertEqual(200, self.curl.getinfo(pycurl.HTTP_CODE))
body = sio.getvalue()
returned_fields = json.loads(body)
self.assertEqual(pf, returned_fields)
--
1.7.1
From 3ea332ffec21dfe6ddf3e0bc66a101259f88f216 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 01:32:33 -0500
Subject: [PATCH 057/149] Fix regular expression to allow all hex chars like it should
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/internals_test.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/tests/internals_test.py b/tests/internals_test.py
index fb451df..f628ab2 100644
--- a/tests/internals_test.py
+++ b/tests/internals_test.py
@@ -180,7 +180,7 @@ class InternalsTest(unittest.TestCase):
del m, c
def test_cyclic_gc(self):
- regexp = re.compile(r'at (0x\d+)')
+ regexp = re.compile(r'at (0x[\da-f]+)')
gc.collect()
c = pycurl.Curl()
c.m = pycurl.CurlMulti()
--
1.7.1
From 990569ee36eac37c03f28c86b2a8cfc7a29752ab Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 01:36:48 -0500
Subject: [PATCH 058/149] Rewrite cyclic gc test to use id()
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/internals_test.py | 9 ++-------
1 files changed, 2 insertions(+), 7 deletions(-)
diff --git a/tests/internals_test.py b/tests/internals_test.py
index f628ab2..0133da0 100644
--- a/tests/internals_test.py
+++ b/tests/internals_test.py
@@ -12,7 +12,6 @@ except ImportError:
import pickle
import gc
import copy
-import re
class InternalsTest(unittest.TestCase):
def setUp(self):
@@ -180,7 +179,6 @@ class InternalsTest(unittest.TestCase):
del m, c
def test_cyclic_gc(self):
- regexp = re.compile(r'at (0x[\da-f]+)')
gc.collect()
c = pycurl.Curl()
c.m = pycurl.CurlMulti()
@@ -208,17 +206,14 @@ class InternalsTest(unittest.TestCase):
##print gc.get_objects()
#if opts.verbose >= 1:
#print("Tracked objects:", len(gc.get_objects()))
- match = regexp.search(repr(c))
- assert match is not None
- address = match.group(1)
+ c_id = id(c)
# The `del' below should delete these 4 objects:
# Curl + internal dict, CurlMulti + internal dict
del c
gc.collect()
objects = gc.get_objects()
- search = 'at %s' % address
for object in objects:
- assert search not in repr(object)
+ assert id(object) != c_id
#if opts.verbose >= 1:
#print("Tracked objects:", len(gc.get_objects()))
--
1.7.1
From 3b7bb39d6b33031ff1efd03abdc174caa32ef3bc Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 03:25:34 -0500
Subject: [PATCH 059/149] Adjust ftp test to work with vsftpd started from project root
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/ftp_test.py | 18 ++++++++++--------
1 files changed, 10 insertions(+), 8 deletions(-)
diff --git a/tests/ftp_test.py b/tests/ftp_test.py
index d215b6e..1d382ed 100644
--- a/tests/ftp_test.py
+++ b/tests/ftp_test.py
@@ -2,6 +2,8 @@
# -*- coding: iso-8859-1 -*-
# vi:ts=4:et
+# Note: this test is meant to be run from pycurl project root.
+
import pycurl
import unittest
@@ -15,29 +17,29 @@ class FtpTest(unittest.TestCase):
self.curl.close()
def test_get_ftp(self):
- self.curl.setopt(pycurl.URL, 'ftp://localhost:8921')
+ self.curl.setopt(pycurl.URL, 'ftp://localhost:8321')
sio = util.StringIO()
self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
self.curl.perform()
result = sio.getvalue()
assert 'README' in result
- assert 'bin -> usr/bin' in result
+ assert 'INSTALL' in result
# XXX this test needs to be fixed
def test_quote(self):
- self.curl.setopt(pycurl.URL, 'ftp://localhost:8921')
+ self.curl.setopt(pycurl.URL, 'ftp://localhost:8321')
sio = util.StringIO()
self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
- self.curl.setopt(pycurl.QUOTE, ['CWD pub'])
+ self.curl.setopt(pycurl.QUOTE, ['CWD tests'])
self.curl.perform()
result = sio.getvalue()
- assert 'README' in result
- assert 'bin -> usr/bin' in result
+ assert 'README' not in result
+ assert 'ftp_test.py' in result
def test_epsv(self):
- self.curl.setopt(pycurl.URL, 'ftp://localhost:8921')
+ self.curl.setopt(pycurl.URL, 'ftp://localhost:8321')
sio = util.StringIO()
self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
self.curl.setopt(pycurl.FTP_USE_EPSV, 1)
@@ -45,4 +47,4 @@ class FtpTest(unittest.TestCase):
result = sio.getvalue()
assert 'README' in result
- assert 'bin -> usr/bin' in result
+ assert 'INSTALL' in result
--
1.7.1
From ce0c221f9db95029982619afd5cc154d68a97334 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 04:01:13 -0500
Subject: [PATCH 060/149] Execute vsftpd as test ftp server
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/ftp_test.py | 3 ++
tests/procmgr.py | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++
tests/runwsgi.py | 20 ++++++++------
tests/vsftpd.conf | 10 +++++++
4 files changed, 98 insertions(+), 8 deletions(-)
create mode 100644 tests/procmgr.py
create mode 100644 tests/vsftpd.conf
diff --git a/tests/ftp_test.py b/tests/ftp_test.py
index 1d382ed..fa2ef79 100644
--- a/tests/ftp_test.py
+++ b/tests/ftp_test.py
@@ -8,6 +8,9 @@ import pycurl
import unittest
from . import util
+from . import procmgr
+
+setup_module, teardown_module = procmgr.vsftpd_setup()
class FtpTest(unittest.TestCase):
def setUp(self):
diff --git a/tests/procmgr.py b/tests/procmgr.py
new file mode 100644
index 0000000..8d5c0cc
--- /dev/null
+++ b/tests/procmgr.py
@@ -0,0 +1,73 @@
+import threading
+import subprocess
+import os
+import signal
+
+from . import runwsgi
+
+class ProcessManager(object):
+ def __init__(self, cmd):
+ self.cmd = cmd
+
+ def start(self):
+ self.process = subprocess.Popen(self.cmd)
+
+ self.thread = threading.Thread(target=self.run)
+ self.thread.daemon = True
+ self.thread.start()
+
+ def run(self):
+ self.process.communicate()
+
+managers = {}
+
+def start(cmd):
+ if str(cmd) in managers:
+ # already started
+ return
+
+ manager = ProcessManager(cmd)
+ managers[str(cmd)] = manager
+ manager.start()
+
+def start_setup(cmd):
+ def do_start():
+ start(cmd)
+ return do_start
+
+# Example on FreeBSD:
+# PYCURL_VSFTPD_PATH=/usr/local/libexec/vsftpd nosetests
+
+if 'PYCURL_VSFTPD_PATH' in os.environ:
+ vsftpd_path = os.environ['PYCURL_VSFTPD_PATH']
+else:
+ vsftpd_path = 'vsftpd'
+
+def vsftpd_setup():
+ config_file_path = os.path.join(os.path.dirname(__file__), 'vsftpd.conf')
+ root_path = os.path.join(os.path.dirname(__file__), '..')
+ cmd = [
+ vsftpd_path,
+ config_file_path,
+ '-oanon_root=%s' % root_path,
+ ]
+ setup_module = start_setup(cmd)
+ def do_setup_module():
+ setup_module()
+ ok = runwsgi.wait_for_network_service(('127.0.0.1', 8321), 0.1, 10)
+ if not ok:
+ import warnings
+ warnings.warn('vsftpd did not start after 1 second')
+
+ def teardown_module():
+ try:
+ manager = managers[str(cmd)]
+ except KeyError:
+ pass
+ else:
+ try:
+ os.kill(manager.process.pid, signal.SIGTERM)
+ except OSError:
+ pass
+
+ return do_setup_module, teardown_module
diff --git a/tests/runwsgi.py b/tests/runwsgi.py
index 6d3b69f..5217a3f 100644
--- a/tests/runwsgi.py
+++ b/tests/runwsgi.py
@@ -16,21 +16,25 @@ class Server(bottle.WSGIRefServer):
self.srv = make_server(self.host, self.port, handler, **self.options)
self.srv.serve_forever(poll_interval=0.1)
-def start_bottle_server(app, port, **kwargs):
- server_thread = ServerThread(app, port, kwargs)
- server_thread.daemon = True
- server_thread.start()
-
+def wait_for_network_service(netloc, check_interval, num_attempts):
ok = False
- for i in range(10):
+ for i in range(num_attempts):
try:
- conn = socket.create_connection(('127.0.0.1', port), 0.1)
+ conn = socket.create_connection(netloc, check_interval)
except socket.error as e:
- _time.sleep(0.1)
+ _time.sleep(check_interval)
else:
conn.close()
ok = True
break
+ return ok
+
+def start_bottle_server(app, port, **kwargs):
+ server_thread = ServerThread(app, port, kwargs)
+ server_thread.daemon = True
+ server_thread.start()
+
+ ok = wait_for_network_service(('127.0.0.1', port), 0.1, 10)
if not ok:
import warnings
warnings.warn('Server did not start after 1 second')
diff --git a/tests/vsftpd.conf b/tests/vsftpd.conf
new file mode 100644
index 0000000..0abb39f
--- /dev/null
+++ b/tests/vsftpd.conf
@@ -0,0 +1,10 @@
+anon_world_readable_only=yes
+anonymous_enable=yes
+# currently we only list files
+download_enable=no
+listen=yes
+run_as_launching_user=yes
+write_enable=no
+listen_port=8321
+# should be supplied on command line
+anon_root=/var/empty
--
1.7.1
From 0457d78a5bd77c11ac67698f125c596f16f05da6 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 00:37:19 -0500
Subject: [PATCH 061/149] Move gtk and xmlrpc tests to examples as they do not test anything not already tested by the test suite
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
examples/tests/test_gtk.py | 98 +++++++++++++++++++++++++++++++++++++++++
examples/tests/test_xmlrpc.py | 29 ++++++++++++
tests/test_gtk.py | 98 -----------------------------------------
tests/test_xmlrpc.py | 29 ------------
4 files changed, 127 insertions(+), 127 deletions(-)
create mode 100644 examples/tests/test_gtk.py
create mode 100644 examples/tests/test_xmlrpc.py
delete mode 100644 tests/test_gtk.py
delete mode 100644 tests/test_xmlrpc.py
diff --git a/examples/tests/test_gtk.py b/examples/tests/test_gtk.py
new file mode 100644
index 0000000..7104439
--- /dev/null
+++ b/examples/tests/test_gtk.py
@@ -0,0 +1,98 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id$
+
+import sys, threading
+import pycurl
+import pygtk
+pygtk.require('2.0')
+import gtk
+
+# We should ignore SIGPIPE when using pycurl.NOSIGNAL - see
+# the libcurl tutorial for more info.
+try:
+ import signal
+ from signal import SIGPIPE, SIG_IGN
+ signal.signal(signal.SIGPIPE, signal.SIG_IGN)
+except ImportError:
+ pass
+
+
+class ProgressBar:
+ def __init__(self, uri):
+ self.round = 0.0
+ win = gtk.Window(gtk.WINDOW_TOPLEVEL)
+ win.set_title("PycURL progress")
+ win.show()
+ vbox = gtk.VBox(spacing=5)
+ vbox.set_border_width(10)
+ win.add(vbox)
+ win.set_default_size(200, 20)
+ vbox.show()
+ label = gtk.Label("Downloading %s" % uri)
+ label.set_alignment(0, 0.5)
+ vbox.pack_start(label)
+ label.show()
+ pbar = gtk.ProgressBar()
+ pbar.show()
+ self.pbar = pbar
+ vbox.pack_start(pbar)
+ win.connect("destroy", self.close_app)
+
+ def progress(self, download_t, download_d, upload_t, upload_d):
+ if download_t == 0:
+ self.round = self.round + 0.1
+ if self.round >= 1.0: self.round = 0.0
+ else:
+ self.round = float(download_d) / float(download_t)
+ gtk.threads_enter()
+ self.pbar.set_fraction(self.round)
+ gtk.threads_leave()
+
+ def mainloop(self):
+ gtk.threads_enter()
+ gtk.main()
+ gtk.threads_leave()
+
+ def close_app(self, *args):
+ args[0].destroy()
+ gtk.main_quit()
+
+
+class Test(threading.Thread):
+ def __init__(self, url, target_file, progress):
+ threading.Thread.__init__(self)
+ self.target_file = target_file
+ self.progress = progress
+ self.curl = pycurl.Curl()
+ self.curl.setopt(pycurl.URL, url)
+ self.curl.setopt(pycurl.WRITEDATA, self.target_file)
+ self.curl.setopt(pycurl.FOLLOWLOCATION, 1)
+ self.curl.setopt(pycurl.NOPROGRESS, 0)
+ self.curl.setopt(pycurl.PROGRESSFUNCTION, self.progress)
+ self.curl.setopt(pycurl.MAXREDIRS, 5)
+ self.curl.setopt(pycurl.NOSIGNAL, 1)
+
+ def run(self):
+ self.curl.perform()
+ self.curl.close()
+ self.target_file.close()
+ self.progress(1.0, 1.0, 0, 0)
+
+
+# Check command line args
+if len(sys.argv) < 3:
+ print "Usage: %s <URL> <filename>" % sys.argv[0]
+ raise SystemExit
+
+# Make a progress bar window
+p = ProgressBar(sys.argv[1])
+# Start thread for fetching url
+Test(sys.argv[1], open(sys.argv[2], 'wb'), p.progress).start()
+# Enter the GTK mainloop
+gtk.threads_init()
+try:
+ p.mainloop()
+except KeyboardInterrupt:
+ pass
diff --git a/examples/tests/test_xmlrpc.py b/examples/tests/test_xmlrpc.py
new file mode 100644
index 0000000..bc5953e
--- /dev/null
+++ b/examples/tests/test_xmlrpc.py
@@ -0,0 +1,29 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id$
+
+## XML-RPC lib included in python2.2
+import xmlrpclib
+import pycurl
+
+# Header fields passed in request
+xmlrpc_header = [
+ "User-Agent: PycURL XML-RPC Test", "Content-Type: text/xml"
+ ]
+
+# XML-RPC request template
+xmlrpc_template = """
+<?xml version='1.0'?><methodCall><methodName>%s</methodName>%s</methodCall>
+"""
+
+# Engage
+c = pycurl.Curl()
+c.setopt(c.URL, 'http://betty.userland.com/RPC2')
+c.setopt(c.POST, 1)
+c.setopt(c.HTTPHEADER, xmlrpc_header)
+c.setopt(c.POSTFIELDS, xmlrpc_template % ("examples.getStateName", xmlrpclib.dumps((5,))))
+
+print 'Response from http://betty.userland.com/'
+c.perform()
+c.close()
diff --git a/tests/test_gtk.py b/tests/test_gtk.py
deleted file mode 100644
index 7104439..0000000
--- a/tests/test_gtk.py
+++ /dev/null
@@ -1,98 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-# vi:ts=4:et
-# $Id$
-
-import sys, threading
-import pycurl
-import pygtk
-pygtk.require('2.0')
-import gtk
-
-# We should ignore SIGPIPE when using pycurl.NOSIGNAL - see
-# the libcurl tutorial for more info.
-try:
- import signal
- from signal import SIGPIPE, SIG_IGN
- signal.signal(signal.SIGPIPE, signal.SIG_IGN)
-except ImportError:
- pass
-
-
-class ProgressBar:
- def __init__(self, uri):
- self.round = 0.0
- win = gtk.Window(gtk.WINDOW_TOPLEVEL)
- win.set_title("PycURL progress")
- win.show()
- vbox = gtk.VBox(spacing=5)
- vbox.set_border_width(10)
- win.add(vbox)
- win.set_default_size(200, 20)
- vbox.show()
- label = gtk.Label("Downloading %s" % uri)
- label.set_alignment(0, 0.5)
- vbox.pack_start(label)
- label.show()
- pbar = gtk.ProgressBar()
- pbar.show()
- self.pbar = pbar
- vbox.pack_start(pbar)
- win.connect("destroy", self.close_app)
-
- def progress(self, download_t, download_d, upload_t, upload_d):
- if download_t == 0:
- self.round = self.round + 0.1
- if self.round >= 1.0: self.round = 0.0
- else:
- self.round = float(download_d) / float(download_t)
- gtk.threads_enter()
- self.pbar.set_fraction(self.round)
- gtk.threads_leave()
-
- def mainloop(self):
- gtk.threads_enter()
- gtk.main()
- gtk.threads_leave()
-
- def close_app(self, *args):
- args[0].destroy()
- gtk.main_quit()
-
-
-class Test(threading.Thread):
- def __init__(self, url, target_file, progress):
- threading.Thread.__init__(self)
- self.target_file = target_file
- self.progress = progress
- self.curl = pycurl.Curl()
- self.curl.setopt(pycurl.URL, url)
- self.curl.setopt(pycurl.WRITEDATA, self.target_file)
- self.curl.setopt(pycurl.FOLLOWLOCATION, 1)
- self.curl.setopt(pycurl.NOPROGRESS, 0)
- self.curl.setopt(pycurl.PROGRESSFUNCTION, self.progress)
- self.curl.setopt(pycurl.MAXREDIRS, 5)
- self.curl.setopt(pycurl.NOSIGNAL, 1)
-
- def run(self):
- self.curl.perform()
- self.curl.close()
- self.target_file.close()
- self.progress(1.0, 1.0, 0, 0)
-
-
-# Check command line args
-if len(sys.argv) < 3:
- print "Usage: %s <URL> <filename>" % sys.argv[0]
- raise SystemExit
-
-# Make a progress bar window
-p = ProgressBar(sys.argv[1])
-# Start thread for fetching url
-Test(sys.argv[1], open(sys.argv[2], 'wb'), p.progress).start()
-# Enter the GTK mainloop
-gtk.threads_init()
-try:
- p.mainloop()
-except KeyboardInterrupt:
- pass
diff --git a/tests/test_xmlrpc.py b/tests/test_xmlrpc.py
deleted file mode 100644
index bc5953e..0000000
--- a/tests/test_xmlrpc.py
+++ /dev/null
@@ -1,29 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-# vi:ts=4:et
-# $Id$
-
-## XML-RPC lib included in python2.2
-import xmlrpclib
-import pycurl
-
-# Header fields passed in request
-xmlrpc_header = [
- "User-Agent: PycURL XML-RPC Test", "Content-Type: text/xml"
- ]
-
-# XML-RPC request template
-xmlrpc_template = """
-<?xml version='1.0'?><methodCall><methodName>%s</methodName>%s</methodCall>
-"""
-
-# Engage
-c = pycurl.Curl()
-c.setopt(c.URL, 'http://betty.userland.com/RPC2')
-c.setopt(c.POST, 1)
-c.setopt(c.HTTPHEADER, xmlrpc_header)
-c.setopt(c.POSTFIELDS, xmlrpc_template % ("examples.getStateName", xmlrpclib.dumps((5,))))
-
-print 'Response from http://betty.userland.com/'
-c.perform()
-c.close()
--
1.7.1
From 25e7d646b9d5c80e8987528bf1b0031622ab440b Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 00:37:41 -0500
Subject: [PATCH 062/149] Delete old tests
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/test.py | 74 -----------
tests/test_cb.py | 28 ----
tests/test_debug.py | 16 ---
tests/test_ftp.py | 13 --
tests/test_getinfo.py | 49 -------
tests/test_internals.py | 258 ------------------------------------
tests/test_memleak.py | 53 --------
tests/test_multi.py | 33 -----
tests/test_multi2.py | 72 ----------
tests/test_multi3.py | 87 ------------
tests/test_multi4.py | 57 --------
tests/test_multi5.py | 60 ---------
tests/test_multi6.py | 62 ---------
tests/test_multi_socket.py | 82 ------------
tests/test_multi_socket_select.py | 105 ---------------
tests/test_multi_timer.py | 76 -----------
tests/test_multi_vs_thread.py | 262 -------------------------------------
tests/test_post.py | 24 ----
tests/test_post2.py | 18 ---
tests/test_post3.py | 32 -----
tests/test_reset.py | 75 -----------
tests/test_share.py | 34 -----
tests/test_socketopen.py | 17 ---
tests/test_stringio.py | 25 ----
24 files changed, 0 insertions(+), 1612 deletions(-)
delete mode 100644 tests/test.py
delete mode 100644 tests/test_cb.py
delete mode 100644 tests/test_debug.py
delete mode 100644 tests/test_ftp.py
delete mode 100644 tests/test_getinfo.py
delete mode 100644 tests/test_internals.py
delete mode 100644 tests/test_memleak.py
delete mode 100644 tests/test_multi.py
delete mode 100644 tests/test_multi2.py
delete mode 100644 tests/test_multi3.py
delete mode 100644 tests/test_multi4.py
delete mode 100644 tests/test_multi5.py
delete mode 100644 tests/test_multi6.py
delete mode 100644 tests/test_multi_socket.py
delete mode 100644 tests/test_multi_socket_select.py
delete mode 100644 tests/test_multi_timer.py
delete mode 100644 tests/test_multi_vs_thread.py
delete mode 100644 tests/test_post.py
delete mode 100644 tests/test_post2.py
delete mode 100644 tests/test_post3.py
delete mode 100644 tests/test_reset.py
delete mode 100644 tests/test_share.py
delete mode 100644 tests/test_socketopen.py
delete mode 100644 tests/test_stringio.py
diff --git a/tests/test.py b/tests/test.py
deleted file mode 100644
index 5cd9740..0000000
--- a/tests/test.py
+++ /dev/null
@@ -1,74 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-# vi:ts=4:et
-# $Id$
-
-import sys, threading, time
-import pycurl
-
-# We should ignore SIGPIPE when using pycurl.NOSIGNAL - see
-# the libcurl tutorial for more info.
-try:
- import signal
- from signal import SIGPIPE, SIG_IGN
- signal.signal(signal.SIGPIPE, signal.SIG_IGN)
-except ImportError:
- pass
-
-
-class Test(threading.Thread):
- def __init__(self, url, ofile):
- threading.Thread.__init__(self)
- self.curl = pycurl.Curl()
- self.curl.setopt(pycurl.URL, url)
- self.curl.setopt(pycurl.WRITEDATA, ofile)
- self.curl.setopt(pycurl.FOLLOWLOCATION, 1)
- self.curl.setopt(pycurl.MAXREDIRS, 5)
- self.curl.setopt(pycurl.NOSIGNAL, 1)
-
- def run(self):
- self.curl.perform()
- self.curl.close()
- sys.stdout.write(".")
- sys.stdout.flush()
-
-
-# Read list of URIs from file specified on commandline
-try:
- urls = open(sys.argv[1]).readlines()
-except IndexError:
- # No file was specified, show usage string
- print "Usage: %s <file with uris to fetch>" % sys.argv[0]
- raise SystemExit
-
-# Initialize thread array and the file number
-threads = []
-fileno = 0
-
-# Start one thread per URI in parallel
-t1 = time.time()
-for url in urls:
- f = open(str(fileno), "wb")
- t = Test(url.rstrip(), f)
- t.start()
- threads.append((t, f))
- fileno = fileno + 1
-# Wait for all threads to finish
-for thread, file in threads:
- thread.join()
- file.close()
-t2 = time.time()
-print "\n** Multithreading, %d seconds elapsed for %d uris" % (int(t2-t1), len(urls))
-
-# Start one thread per URI in sequence
-fileno = 0
-t1 = time.time()
-for url in urls:
- f = open(str(fileno), "wb")
- t = Test(url.rstrip(), f)
- t.start()
- fileno = fileno + 1
- t.join()
- f.close()
-t2 = time.time()
-print "\n** Singlethreading, %d seconds elapsed for %d uris" % (int(t2-t1), len(urls))
diff --git a/tests/test_cb.py b/tests/test_cb.py
deleted file mode 100644
index 1be305c..0000000
--- a/tests/test_cb.py
+++ /dev/null
@@ -1,28 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-# vi:ts=4:et
-# $Id$
-
-import sys
-import pycurl
-
-## Callback function invoked when body data is ready
-def body(buf):
- # Print body data to stdout
- sys.stdout.write(buf)
-
-## Callback function invoked when header data is ready
-def header(buf):
- # Print header data to stderr
- sys.stderr.write(buf)
-
-c = pycurl.Curl()
-c.setopt(pycurl.URL, 'http://www.python.org/')
-c.setopt(pycurl.WRITEFUNCTION, body)
-c.setopt(pycurl.HEADERFUNCTION, header)
-c.setopt(pycurl.FOLLOWLOCATION, 1)
-c.setopt(pycurl.MAXREDIRS, 5)
-c.perform()
-c.setopt(pycurl.URL, 'http://curl.haxx.se/')
-c.perform()
-c.close()
diff --git a/tests/test_debug.py b/tests/test_debug.py
deleted file mode 100644
index d439b16..0000000
--- a/tests/test_debug.py
+++ /dev/null
@@ -1,16 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-# vi:ts=4:et
-# $Id$
-
-import pycurl
-
-def test(t, b):
- print "debug(%d): %s" % (t, b)
-
-c = pycurl.Curl()
-c.setopt(pycurl.URL, 'http://curl.haxx.se/')
-c.setopt(pycurl.VERBOSE, 1)
-c.setopt(pycurl.DEBUGFUNCTION, test)
-c.perform()
-c.close()
diff --git a/tests/test_ftp.py b/tests/test_ftp.py
deleted file mode 100644
index 2d4d358..0000000
--- a/tests/test_ftp.py
+++ /dev/null
@@ -1,13 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-# vi:ts=4:et
-# $Id$
-
-import pycurl
-
-c = pycurl.Curl()
-c.setopt(c.URL, 'ftp://ftp.sunet.se/')
-c.setopt(c.FTP_USE_EPSV, 1)
-c.setopt(c.QUOTE, ['cwd pub', 'type i'])
-c.perform()
-c.close()
diff --git a/tests/test_getinfo.py b/tests/test_getinfo.py
deleted file mode 100644
index ed64594..0000000
--- a/tests/test_getinfo.py
+++ /dev/null
@@ -1,49 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-# vi:ts=4:et
-# $Id$
-
-import time
-import pycurl
-
-
-## Callback function invoked when progress information is updated
-def progress(download_t, download_d, upload_t, upload_d):
- print "Total to download %d bytes, have %d bytes so far" % \
- (download_t, download_d)
-
-url = "http://www.cnn.com"
-
-print "Starting downloading", url
-print
-f = open("body", "wb")
-h = open("header", "wb")
-c = pycurl.Curl()
-c.setopt(c.URL, url)
-c.setopt(c.WRITEDATA, f)
-c.setopt(c.NOPROGRESS, 0)
-c.setopt(c.PROGRESSFUNCTION, progress)
-c.setopt(c.FOLLOWLOCATION, 1)
-c.setopt(c.MAXREDIRS, 5)
-c.setopt(c.WRITEHEADER, h)
-c.setopt(c.OPT_FILETIME, 1)
-c.perform()
-
-print
-print "HTTP-code:", c.getinfo(c.HTTP_CODE)
-print "Total-time:", c.getinfo(c.TOTAL_TIME)
-print "Download speed: %.2f bytes/second" % c.getinfo(c.SPEED_DOWNLOAD)
-print "Document size: %d bytes" % c.getinfo(c.SIZE_DOWNLOAD)
-print "Effective URL:", c.getinfo(c.EFFECTIVE_URL)
-print "Content-type:", c.getinfo(c.CONTENT_TYPE)
-print "Namelookup-time:", c.getinfo(c.NAMELOOKUP_TIME)
-print "Redirect-time:", c.getinfo(c.REDIRECT_TIME)
-print "Redirect-count:", c.getinfo(c.REDIRECT_COUNT)
-epoch = c.getinfo(c.INFO_FILETIME)
-print "Filetime: %d (%s)" % (epoch, time.ctime(epoch))
-print
-print "Header is in file 'header', body is in file 'body'"
-
-c.close()
-f.close()
-h.close()
diff --git a/tests/test_internals.py b/tests/test_internals.py
deleted file mode 100644
index 3f5eefd..0000000
--- a/tests/test_internals.py
+++ /dev/null
@@ -1,258 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-# vi:ts=4:et
-# $Id$
-
-#
-# a simple self-test
-#
-
-try:
- # need Python 2.2 or better for garbage collection
- from gc import get_objects
- import gc
- del get_objects
- gc.enable()
-except ImportError:
- gc = None
-import copy, os, sys
-from StringIO import StringIO
-try:
- import cPickle
-except ImportError:
- cPickle = None
-try:
- import pickle
-except ImportError:
- pickle = None
-
-# update sys.path when running in the build directory
-from util import get_sys_path
-sys.path = get_sys_path()
-
-import pycurl
-from pycurl import Curl, CurlMulti
-
-
-class opts:
- verbose = 1
-
-if "-q" in sys.argv:
- opts.verbose = opts.verbose - 1
-
-
-print "Python", sys.version
-print "PycURL %s (compiled against 0x%x)" % (pycurl.version, pycurl.COMPILE_LIBCURL_VERSION_NUM)
-print "PycURL version info", pycurl.version_info()
-print " %s, compiled %s" % (pycurl.__file__, pycurl.COMPILE_DATE)
-
-
-# /***********************************************************************
-# // test misc
-# ************************************************************************/
-
-if 1:
- c = Curl()
- assert c.URL is pycurl.URL
- del c
-
-
-# /***********************************************************************
-# // test handles
-# ************************************************************************/
-
-# remove an invalid handle: this should fail
-if 1:
- m = CurlMulti()
- c = Curl()
- try:
- m.remove_handle(c)
- except pycurl.error:
- pass
- else:
- assert 0, "internal error"
- del m, c
-
-
-# remove an invalid but closed handle
-if 1:
- m = CurlMulti()
- c = Curl()
- c.close()
- m.remove_handle(c)
- del m, c
-
-
-# add a closed handle: this should fail
-if 1:
- m = CurlMulti()
- c = Curl()
- c.close()
- try:
- m.add_handle(c)
- except pycurl.error:
- pass
- else:
- assert 0, "internal error"
- m.close()
- del m, c
-
-
-# add a handle twice: this should fail
-if 1:
- m = CurlMulti()
- c = Curl()
- m.add_handle(c)
- try:
- m.add_handle(c)
- except pycurl.error:
- pass
- else:
- assert 0, "internal error"
- del m, c
-
-
-# add a handle on multiple stacks: this should fail
-if 1:
- m1 = CurlMulti()
- m2 = CurlMulti()
- c = Curl()
- m1.add_handle(c)
- try:
- m2.add_handle(c)
- except pycurl.error:
- pass
- else:
- assert 0, "internal error"
- del m1, m2, c
-
-
-# move a handle
-if 1:
- m1 = CurlMulti()
- m2 = CurlMulti()
- c = Curl()
- m1.add_handle(c)
- m1.remove_handle(c)
- m2.add_handle(c)
- del m1, m2, c
-
-
-# /***********************************************************************
-# // test copying and pickling - copying and pickling of
-# // instances of Curl and CurlMulti is not allowed
-# ************************************************************************/
-
-if 1 and copy:
- c = Curl()
- m = CurlMulti()
- try:
- copy.copy(c)
- except copy.Error:
- pass
- else:
- assert 0, "internal error - copying should fail"
- try:
- copy.copy(m)
- except copy.Error:
- pass
- else:
- assert 0, "internal error - copying should fail"
-
-if 1 and pickle:
- c = Curl()
- m = CurlMulti()
- fp = StringIO()
- p = pickle.Pickler(fp, 1)
- try:
- p.dump(c)
- except pickle.PicklingError:
- pass
- else:
- assert 0, "internal error - pickling should fail"
- try:
- p.dump(m)
- except pickle.PicklingError:
- pass
- else:
- assert 0, "internal error - pickling should fail"
- del c, m, fp, p
-
-if 1 and cPickle:
- c = Curl()
- m = CurlMulti()
- fp = StringIO()
- p = cPickle.Pickler(fp, 1)
- try:
- p.dump(c)
- except cPickle.PicklingError:
- pass
- else:
- assert 0, "internal error - pickling should fail"
- try:
- p.dump(m)
- except cPickle.PicklingError:
- pass
- else:
- assert 0, "internal error - pickling should fail"
- del c, m, fp, p
-
-
-# /***********************************************************************
-# // test refcounts
-# ************************************************************************/
-
-# basic check of reference counting (use a memory checker like valgrind)
-if 1:
- c = Curl()
- m = CurlMulti()
- m.add_handle(c)
- del m
- m = CurlMulti()
- c.close()
- del m, c
-
-# basic check of cyclic garbage collection
-if 1 and gc:
- gc.collect()
- c = Curl()
- c.m = CurlMulti()
- c.m.add_handle(c)
- # create some nasty cyclic references
- c.c = c
- c.c.c1 = c
- c.c.c2 = c
- c.c.c3 = c.c
- c.c.c4 = c.m
- c.m.c = c
- c.m.m = c.m
- c.m.c = c
- # delete
- gc.collect()
- flags = gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_OBJECTS
- if opts.verbose >= 1:
- flags = flags | gc.DEBUG_STATS
- gc.set_debug(flags)
- gc.collect()
- ##print gc.get_referrers(c)
- ##print gc.get_objects()
- if opts.verbose >= 1:
- print "Tracked objects:", len(gc.get_objects())
- # The `del' below should delete these 4 objects:
- # Curl + internal dict, CurlMulti + internal dict
- del c
- gc.collect()
- if opts.verbose >= 1:
- print "Tracked objects:", len(gc.get_objects())
-
-if 1:
- # Ensure that the refcounting error in "reset" is fixed:
- for i in xrange(100000):
- c = Curl()
- c.reset()
-
-# /***********************************************************************
-# // done
-# ************************************************************************/
-
-print "All tests passed."
diff --git a/tests/test_memleak.py b/tests/test_memleak.py
deleted file mode 100644
index 8577b97..0000000
--- a/tests/test_memleak.py
+++ /dev/null
@@ -1,53 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-# vi:ts=4:et
-# $Id$
-
-#
-# just a simple self-test
-# need Python 2.2 or better for garbage collection
-#
-
-import gc, pycurl, sys
-gc.enable()
-
-
-print "Python", sys.version
-print "PycURL %s (compiled against 0x%x)" % (pycurl.version, pycurl.COMPILE_LIBCURL_VERSION_NUM)
-##print "PycURL version info", pycurl.version_info()
-print " %s, compiled %s" % (pycurl.__file__, pycurl.COMPILE_DATE)
-
-
-gc.collect()
-flags = gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_OBJECTS
-if 1:
- flags = flags | gc.DEBUG_STATS
-gc.set_debug(flags)
-gc.collect()
-
-print "Tracked objects:", len(gc.get_objects())
-
-multi = pycurl.CurlMulti()
-t = []
-for a in range(100):
- curl = pycurl.Curl()
- multi.add_handle(curl)
- t.append(curl)
-
-print "Tracked objects:", len(gc.get_objects())
-
-for curl in t:
- curl.close()
- multi.remove_handle(curl)
-
-print "Tracked objects:", len(gc.get_objects())
-
-del curl
-del t
-del multi
-
-print "Tracked objects:", len(gc.get_objects())
-gc.collect()
-print "Tracked objects:", len(gc.get_objects())
-
-
diff --git a/tests/test_multi.py b/tests/test_multi.py
deleted file mode 100644
index 9193986..0000000
--- a/tests/test_multi.py
+++ /dev/null
@@ -1,33 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-# vi:ts=4:et
-# $Id$
-
-import pycurl
-
-m = pycurl.CurlMulti()
-m.handles = []
-c1 = pycurl.Curl()
-c2 = pycurl.Curl()
-c1.setopt(c1.URL, 'http://curl.haxx.se')
-c2.setopt(c2.URL, 'http://cnn.com')
-c2.setopt(c2.FOLLOWLOCATION, 1)
-m.add_handle(c1)
-m.add_handle(c2)
-m.handles.append(c1)
-m.handles.append(c2)
-
-num_handles = len(m.handles)
-while num_handles:
- while 1:
- ret, num_handles = m.perform()
- if ret != pycurl.E_CALL_MULTI_PERFORM:
- break
- m.select(1.0)
-
-m.remove_handle(c2)
-m.remove_handle(c1)
-del m.handles
-m.close()
-c1.close()
-c2.close()
diff --git a/tests/test_multi2.py b/tests/test_multi2.py
deleted file mode 100644
index 4b96789..0000000
--- a/tests/test_multi2.py
+++ /dev/null
@@ -1,72 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-# vi:ts=4:et
-# $Id$
-
-import os, sys
-try:
- from cStringIO import StringIO
-except ImportError:
- from StringIO import StringIO
-import pycurl
-
-
-urls = (
- "http://curl.haxx.se",
- "http://www.python.org",
- "http://pycurl.sourceforge.net",
- "http://pycurl.sourceforge.net/tests/403_FORBIDDEN", # that actually exists ;-)
- "http://pycurl.sourceforge.net/tests/404_NOT_FOUND",
-)
-
-# Read list of URIs from file specified on commandline
-try:
- urls = open(sys.argv[1], "rb").readlines()
-except IndexError:
- # No file was specified
- pass
-
-# init
-m = pycurl.CurlMulti()
-m.handles = []
-for url in urls:
- c = pycurl.Curl()
- # save info in standard Python attributes
- c.url = url.rstrip()
- c.body = StringIO()
- c.http_code = -1
- m.handles.append(c)
- # pycurl API calls
- c.setopt(c.URL, c.url)
- c.setopt(c.WRITEFUNCTION, c.body.write)
- m.add_handle(c)
-
-# get data
-num_handles = len(m.handles)
-while num_handles:
- while 1:
- ret, num_handles = m.perform()
- if ret != pycurl.E_CALL_MULTI_PERFORM:
- break
- # currently no more I/O is pending, could do something in the meantime
- # (display a progress bar, etc.)
- m.select(1.0)
-
-# close handles
-for c in m.handles:
- # save info in standard Python attributes
- c.http_code = c.getinfo(c.HTTP_CODE)
- # pycurl API calls
- m.remove_handle(c)
- c.close()
-m.close()
-
-# print result
-for c in m.handles:
- data = c.body.getvalue()
- if 0:
- print "**********", c.url, "**********"
- print data
- else:
- print "%-53s http_code %3d, %6d bytes" % (c.url, c.http_code, len(data))
-
diff --git a/tests/test_multi3.py b/tests/test_multi3.py
deleted file mode 100644
index 5b7ea6e..0000000
--- a/tests/test_multi3.py
+++ /dev/null
@@ -1,87 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-# vi:ts=4:et
-# $Id$
-
-# same as test_multi2.py, but enforce some debugging and strange API-calls
-
-import os, sys
-try:
- from cStringIO import StringIO
-except ImportError:
- from StringIO import StringIO
-import pycurl
-
-
-urls = (
- "http://curl.haxx.se",
- "http://www.python.org",
- "http://pycurl.sourceforge.net",
- "http://pycurl.sourceforge.net/THIS_HANDLE_IS_CLOSED",
-)
-
-# init
-m = pycurl.CurlMulti()
-m.handles = []
-for url in urls:
- c = pycurl.Curl()
- # save info in standard Python attributes
- c.url = url
- c.body = StringIO()
- c.http_code = -1
- c.debug = 0
- m.handles.append(c)
- # pycurl API calls
- c.setopt(c.URL, c.url)
- c.setopt(c.WRITEFUNCTION, c.body.write)
- m.add_handle(c)
-
-# debug - close a handle
-if 1:
- c = m.handles[3]
- c.debug = 1
- c.close()
-
-# get data
-num_handles = len(m.handles)
-while num_handles:
- while 1:
- ret, num_handles = m.perform()
- if ret != pycurl.E_CALL_MULTI_PERFORM:
- break
- # currently no more I/O is pending, could do something in the meantime
- # (display a progress bar, etc.)
- m.select(1.0)
-
-# close handles
-for c in m.handles:
- # save info in standard Python attributes
- try:
- c.http_code = c.getinfo(c.HTTP_CODE)
- except pycurl.error:
- # handle already closed - see debug above
- assert c.debug
- c.http_code = -1
- # pycurl API calls
- if 0:
- m.remove_handle(c)
- c.close()
- elif 0:
- # in the C API this is the wrong calling order, but pycurl
- # handles this automatically
- c.close()
- m.remove_handle(c)
- else:
- # actually, remove_handle is called automatically on close
- c.close()
-m.close()
-
-# print result
-for c in m.handles:
- data = c.body.getvalue()
- if 0:
- print "**********", c.url, "**********"
- print data
- else:
- print "%-53s http_code %3d, %6d bytes" % (c.url, c.http_code, len(data))
-
diff --git a/tests/test_multi4.py b/tests/test_multi4.py
deleted file mode 100644
index f37ea26..0000000
--- a/tests/test_multi4.py
+++ /dev/null
@@ -1,57 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-# vi:ts=4:et
-# $Id$
-
-import sys, select, time
-import pycurl
-
-c1 = pycurl.Curl()
-c2 = pycurl.Curl()
-c3 = pycurl.Curl()
-c1.setopt(c1.URL, "http://www.python.org")
-c2.setopt(c2.URL, "http://curl.haxx.se")
-c3.setopt(c3.URL, "http://slashdot.org")
-c1.body = open("doc1", "wb")
-c2.body = open("doc2", "wb")
-c3.body = open("doc3", "wb")
-c1.setopt(c1.WRITEFUNCTION, c1.body.write)
-c2.setopt(c2.WRITEFUNCTION, c2.body.write)
-c3.setopt(c3.WRITEFUNCTION, c3.body.write)
-
-m = pycurl.CurlMulti()
-m.add_handle(c1)
-m.add_handle(c2)
-m.add_handle(c3)
-
-# Number of seconds to wait for a timeout to happen
-SELECT_TIMEOUT = 1.0
-
-# Stir the state machine into action
-while 1:
- ret, num_handles = m.perform()
- if ret != pycurl.E_CALL_MULTI_PERFORM:
- break
-
-# Keep going until all the connections have terminated
-while num_handles:
- apply(select.select, m.fdset() + (SELECT_TIMEOUT,))
- while 1:
- ret, num_handles = m.perform()
- if ret != pycurl.E_CALL_MULTI_PERFORM:
- break
-
-# Cleanup
-m.remove_handle(c3)
-m.remove_handle(c2)
-m.remove_handle(c1)
-m.close()
-c1.body.close()
-c2.body.close()
-c3.body.close()
-c1.close()
-c2.close()
-c3.close()
-print "http://www.python.org is in file doc1"
-print "http://curl.haxx.se is in file doc2"
-print "http://slashdot.org is in file doc3"
diff --git a/tests/test_multi5.py b/tests/test_multi5.py
deleted file mode 100644
index 3f0c8df..0000000
--- a/tests/test_multi5.py
+++ /dev/null
@@ -1,60 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-# vi:ts=4:et
-# $Id$
-
-import sys, select, time
-import pycurl
-
-c1 = pycurl.Curl()
-c2 = pycurl.Curl()
-c3 = pycurl.Curl()
-c1.setopt(c1.URL, "http://www.python.org")
-c2.setopt(c2.URL, "http://curl.haxx.se")
-c3.setopt(c3.URL, "http://slashdot.org")
-c1.body = open("doc1", "wb")
-c2.body = open("doc2", "wb")
-c3.body = open("doc3", "wb")
-c1.setopt(c1.WRITEFUNCTION, c1.body.write)
-c2.setopt(c2.WRITEFUNCTION, c2.body.write)
-c3.setopt(c3.WRITEFUNCTION, c3.body.write)
-
-m = pycurl.CurlMulti()
-m.add_handle(c1)
-m.add_handle(c2)
-m.add_handle(c3)
-
-# Number of seconds to wait for a timeout to happen
-SELECT_TIMEOUT = 1.0
-
-# Stir the state machine into action
-while 1:
- ret, num_handles = m.perform()
- if ret != pycurl.E_CALL_MULTI_PERFORM:
- break
-
-# Keep going until all the connections have terminated
-while num_handles:
- # The select method uses fdset internally to determine which file descriptors
- # to check.
- m.select(SELECT_TIMEOUT)
- while 1:
- ret, num_handles = m.perform()
- if ret != pycurl.E_CALL_MULTI_PERFORM:
- break
-
-# Cleanup
-m.remove_handle(c3)
-m.remove_handle(c2)
-m.remove_handle(c1)
-m.close()
-c1.body.close()
-c2.body.close()
-c3.body.close()
-c1.close()
-c2.close()
-c3.close()
-print "http://www.python.org is in file doc1"
-print "http://curl.haxx.se is in file doc2"
-print "http://slashdot.org is in file doc3"
-
diff --git a/tests/test_multi6.py b/tests/test_multi6.py
deleted file mode 100644
index 35a284f..0000000
--- a/tests/test_multi6.py
+++ /dev/null
@@ -1,62 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-# vi:ts=4:et
-# $Id$
-
-import sys, select, time
-import pycurl
-
-c1 = pycurl.Curl()
-c2 = pycurl.Curl()
-c3 = pycurl.Curl()
-c1.setopt(c1.URL, "http://www.python.org")
-c2.setopt(c2.URL, "http://curl.haxx.se")
-c3.setopt(c3.URL, "http://slashdot.org")
-c1.body = open("doc1", "wb")
-c2.body = open("doc2", "wb")
-c3.body = open("doc3", "wb")
-c1.setopt(c1.WRITEFUNCTION, c1.body.write)
-c2.setopt(c2.WRITEFUNCTION, c2.body.write)
-c3.setopt(c3.WRITEFUNCTION, c3.body.write)
-
-m = pycurl.CurlMulti()
-m.add_handle(c1)
-m.add_handle(c2)
-m.add_handle(c3)
-
-# Number of seconds to wait for a timeout to happen
-SELECT_TIMEOUT = 1.0
-
-# Stir the state machine into action
-while 1:
- ret, num_handles = m.perform()
- if ret != pycurl.E_CALL_MULTI_PERFORM:
- break
-
-# Keep going until all the connections have terminated
-while num_handles:
- # The select method uses fdset internally to determine which file descriptors
- # to check.
- m.select(SELECT_TIMEOUT)
- while 1:
- ret, num_handles = m.perform()
- # Print the message, if any
- print m.info_read(1)
- if ret != pycurl.E_CALL_MULTI_PERFORM:
- break
-
-# Cleanup
-m.remove_handle(c3)
-m.remove_handle(c2)
-m.remove_handle(c1)
-m.close()
-c1.body.close()
-c2.body.close()
-c3.body.close()
-c1.close()
-c2.close()
-c3.close()
-print "http://www.python.org is in file doc1"
-print "http://curl.haxx.se is in file doc2"
-print "http://slashdot.org is in file doc3"
-
diff --git a/tests/test_multi_socket.py b/tests/test_multi_socket.py
deleted file mode 100644
index 6768061..0000000
--- a/tests/test_multi_socket.py
+++ /dev/null
@@ -1,82 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-# vi:ts=4:et
-# $Id$
-
-import os, sys
-try:
- from cStringIO import StringIO
-except ImportError:
- from StringIO import StringIO
-import pycurl
-
-
-urls = (
- "http://curl.haxx.se",
- "http://www.python.org",
- "http://pycurl.sourceforge.net",
-)
-
-# Read list of URIs from file specified on commandline
-try:
- urls = open(sys.argv[1], "rb").readlines()
-except IndexError:
- # No file was specified
- pass
-
-# timer callback
-def timer(msecs):
- print 'Timer callback msecs:', msecs
-
-# socket callback
-def socket(event, socket, multi, data):
- print event, socket, multi, data
-# multi.assign(socket, timer)
-
-# init
-m = pycurl.CurlMulti()
-m.setopt(pycurl.M_PIPELINING, 1)
-m.setopt(pycurl.M_TIMERFUNCTION, timer)
-m.setopt(pycurl.M_SOCKETFUNCTION, socket)
-m.handles = []
-for url in urls:
- c = pycurl.Curl()
- # save info in standard Python attributes
- c.url = url
- c.body = StringIO()
- c.http_code = -1
- m.handles.append(c)
- # pycurl API calls
- c.setopt(c.URL, c.url)
- c.setopt(c.WRITEFUNCTION, c.body.write)
- m.add_handle(c)
-
-# get data
-num_handles = len(m.handles)
-while num_handles:
- while 1:
- ret, num_handles = m.socket_all()
- if ret != pycurl.E_CALL_MULTI_PERFORM:
- break
- # currently no more I/O is pending, could do something in the meantime
- # (display a progress bar, etc.)
- m.select(1.0)
-
-# close handles
-for c in m.handles:
- # save info in standard Python attributes
- c.http_code = c.getinfo(c.HTTP_CODE)
- # pycurl API calls
- m.remove_handle(c)
- c.close()
-m.close()
-
-# print result
-for c in m.handles:
- data = c.body.getvalue()
- if 0:
- print "**********", c.url, "**********"
- print data
- else:
- print "%-53s http_code %3d, %6d bytes" % (c.url, c.http_code, len(data))
-
diff --git a/tests/test_multi_socket_select.py b/tests/test_multi_socket_select.py
deleted file mode 100644
index 1f56d1d..0000000
--- a/tests/test_multi_socket_select.py
+++ /dev/null
@@ -1,105 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-# vi:ts=4:et
-# $Id$
-
-import os, sys
-try:
- from cStringIO import StringIO
-except ImportError:
- from StringIO import StringIO
-import pycurl
-import select
-
-sockets = set()
-timeout = 0
-
-urls = (
- "http://curl.haxx.se",
- "http://www.python.org",
- "http://pycurl.sourceforge.net",
-)
-
-# Read list of URIs from file specified on commandline
-try:
- urls = open(sys.argv[1], "rb").readlines()
-except IndexError:
- # No file was specified
- pass
-
-# timer callback
-def timer(msecs):
- global timeout
- timeout = msecs
- print 'Timer callback msecs:', msecs
-
-# socket callback
-def socket(event, socket, multi, data):
- if event == pycurl.POLL_REMOVE:
- print "Remove Socket %d"%socket
- sockets.remove(socket)
- else:
- if socket not in sockets:
- print "Add socket %d"%socket
- sockets.add(socket)
- print event, socket, multi, data
-
-# init
-m = pycurl.CurlMulti()
-m.setopt(pycurl.M_PIPELINING, 1)
-m.setopt(pycurl.M_TIMERFUNCTION, timer)
-m.setopt(pycurl.M_SOCKETFUNCTION, socket)
-m.handles = []
-for url in urls:
- c = pycurl.Curl()
- # save info in standard Python attributes
- c.url = url
- c.body = StringIO()
- c.http_code = -1
- m.handles.append(c)
- # pycurl API calls
- c.setopt(c.URL, c.url)
- c.setopt(c.WRITEFUNCTION, c.body.write)
- m.add_handle(c)
-
-# get data
-num_handles = len(m.handles)
-
-while (pycurl.E_CALL_MULTI_PERFORM==m.socket_all()[0]):
- pass
-
-timeout = m.timeout()
-
-
-while True:
- (rr, wr, er) = select.select(sockets,sockets,sockets,timeout/1000.0)
- socketSet = set(rr+wr+er)
- if socketSet:
- for s in socketSet:
- while True:
- (ret,running) = m.socket_action(s,0)
- if ret!=pycurl.E_CALL_MULTI_PERFORM:
- break
- else:
- (ret,running) = m.socket_action(pycurl.SOCKET_TIMEOUT,0)
- if running==0:
- break
-
-# close handles
-for c in m.handles:
- # save info in standard Python attributes
- c.http_code = c.getinfo(c.HTTP_CODE)
- # pycurl API calls
- m.remove_handle(c)
- c.close()
-m.close()
-
-# print result
-for c in m.handles:
- data = c.body.getvalue()
- if 0:
- print "**********", c.url, "**********"
- print data
- else:
- print "%-53s http_code %3d, %6d bytes" % (c.url, c.http_code, len(data))
-
diff --git a/tests/test_multi_timer.py b/tests/test_multi_timer.py
deleted file mode 100644
index 17371d3..0000000
--- a/tests/test_multi_timer.py
+++ /dev/null
@@ -1,76 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-# vi:ts=4:et
-# $Id$
-
-import os, sys
-try:
- from cStringIO import StringIO
-except ImportError:
- from StringIO import StringIO
-import pycurl
-
-
-urls = (
- "http://curl.haxx.se",
- "http://www.python.org",
- "http://pycurl.sourceforge.net",
-)
-
-# Read list of URIs from file specified on commandline
-try:
- urls = open(sys.argv[1], "rb").readlines()
-except IndexError:
- # No file was specified
- pass
-
-# timer callback
-def timer(msecs):
- print 'Timer callback msecs:', msecs
-
-# init
-m = pycurl.CurlMulti()
-m.setopt(pycurl.M_PIPELINING, 1)
-m.setopt(pycurl.M_TIMERFUNCTION, timer)
-m.handles = []
-for url in urls:
- c = pycurl.Curl()
- # save info in standard Python attributes
- c.url = url
- c.body = StringIO()
- c.http_code = -1
- m.handles.append(c)
- # pycurl API calls
- c.setopt(c.URL, c.url)
- c.setopt(c.WRITEFUNCTION, c.body.write)
- m.add_handle(c)
-
-# get data
-num_handles = len(m.handles)
-while num_handles:
- while 1:
- ret, num_handles = m.perform()
- if ret != pycurl.E_CALL_MULTI_PERFORM:
- break
- # currently no more I/O is pending, could do something in the meantime
- # (display a progress bar, etc.)
- m.select(1.0)
-
-# close handles
-for c in m.handles:
- # save info in standard Python attributes
- c.http_code = c.getinfo(c.HTTP_CODE)
- # pycurl API calls
- m.remove_handle(c)
- c.close()
-m.close()
-
-# print result
-for c in m.handles:
- data = c.body.getvalue()
- if 0:
- print "**********", c.url, "**********"
- print data
- else:
- print "%-53s http_code %3d, %6d bytes" % (c.url, c.http_code, len(data))
-
diff --git a/tests/test_multi_vs_thread.py b/tests/test_multi_vs_thread.py
deleted file mode 100644
index 0caed60..0000000
--- a/tests/test_multi_vs_thread.py
+++ /dev/null
@@ -1,262 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-# vi:ts=4:et
-# $Id$
-
-import os, sys, time
-from threading import Thread, RLock
-try:
- from cStringIO import StringIO
-except ImportError:
- from StringIO import StringIO
-import pycurl
-
-# We should ignore SIGPIPE when using pycurl.NOSIGNAL - see
-# the libcurl tutorial for more info.
-try:
- import signal
- from signal import SIGPIPE, SIG_IGN
- signal.signal(signal.SIGPIPE, signal.SIG_IGN)
-except ImportError:
- pass
-
-# The conclusion is: the multi interface is fastest!
-
-NUM_PAGES = 30
-NUM_THREADS = 10
-assert NUM_PAGES % NUM_THREADS == 0
-
-##URL = "http://pycurl.sourceforge.net/tests/testgetvars.php?%d"
-URL = "http://pycurl.sourceforge.net/tests/teststaticpage.html?%d"
-
-
-#
-# util
-#
-
-class Curl:
- def __init__(self, url):
- self.url = url
- self.body = StringIO()
- self.http_code = -1
- # pycurl API calls
- self._curl = pycurl.Curl()
- self._curl.setopt(pycurl.URL, self.url)
- self._curl.setopt(pycurl.WRITEFUNCTION, self.body.write)
- self._curl.setopt(pycurl.NOSIGNAL, 1)
-
- def perform(self):
- self._curl.perform()
-
- def close(self):
- self.http_code = self._curl.getinfo(pycurl.HTTP_CODE)
- self._curl.close()
-
-
-def print_result(items):
- return # DO NOTHING
- #
- for c in items:
- data = c.body.getvalue()
- if 0:
- print "**********", c.url, "**********"
- print data
- elif 1:
- print "%-60s %3d %6d" % (c.url, c.http_code, len(data))
-
-
-###
-### 1) multi
-###
-
-def test_multi():
- clock1 = time.time()
-
- # init
- handles = []
- m = pycurl.CurlMulti()
- for i in range(NUM_PAGES):
- c = Curl(URL %i)
- m.add_handle(c._curl)
- handles.append(c)
-
- clock2 = time.time()
-
- # stir state machine into action
- while 1:
- ret, num_handles = m.perform()
- if ret != pycurl.E_CALL_MULTI_PERFORM:
- break
-
- # get data
- while num_handles:
- m.select(1.0)
- while 1:
- ret, num_handles = m.perform()
- if ret != pycurl.E_CALL_MULTI_PERFORM:
- break
-
- clock3 = time.time()
-
- # close handles
- for c in handles:
- c.close()
- m.close()
-
- clock4 = time.time()
- print "multi interface: %d pages: perform %5.2f secs, total %5.2f secs" % (NUM_PAGES, clock3 - clock2, clock4 - clock1)
-
- # print result
- print_result(handles)
-
-
-
-###
-### 2) thread
-###
-
-class Test(Thread):
- def __init__(self, lock=None):
- Thread.__init__(self)
- self.lock = lock
- self.items = []
-
- def run(self):
- if self.lock:
- self.lock.acquire()
- self.lock.release()
- for c in self.items:
- c.perform()
-
-
-def test_threads(lock=None):
- clock1 = time.time()
-
- # create and start threads, but block them
- if lock:
- lock.acquire()
-
- # init (FIXME - this is ugly)
- threads = []
- handles = []
- t = None
- for i in range(NUM_PAGES):
- if i % (NUM_PAGES / NUM_THREADS) == 0:
- t = Test(lock)
- if lock:
- t.start()
- threads.append(t)
- c = Curl(URL % i)
- t.items.append(c)
- handles.append(c)
- assert len(handles) == NUM_PAGES
- assert len(threads) == NUM_THREADS
-
- clock2 = time.time()
-
- #
- if lock:
- # release lock to let the blocked threads run
- lock.release()
- else:
- # start threads
- for t in threads:
- t.start()
- # wait for threads to finish
- for t in threads:
- t.join()
-
- clock3 = time.time()
-
- # close handles
- for c in handles:
- c.close()
-
- clock4 = time.time()
- if lock:
- print "thread interface [lock]: %d pages: perform %5.2f secs, total %5.2f secs" % (NUM_PAGES, clock3 - clock2, clock4 - clock1)
- else:
- print "thread interface: %d pages: perform %5.2f secs, total %5.2f secs" % (NUM_PAGES, clock3 - clock2, clock4 - clock1)
-
- # print result
- print_result(handles)
-
-
-
-###
-### 3) thread - threads grab curl objects on demand from a shared pool
-###
-
-class TestPool(Thread):
- def __init__(self, lock, pool):
- Thread.__init__(self)
- self.lock = lock
- self.pool = pool
-
- def run(self):
- while 1:
- self.lock.acquire()
- c = None
- if self.pool:
- c = self.pool.pop()
- self.lock.release()
- if c is None:
- break
- c.perform()
-
-
-def test_thread_pool(lock):
- clock1 = time.time()
-
- # init
- handles = []
- for i in range(NUM_PAGES):
- c = Curl(URL %i)
- handles.append(c)
-
- # create and start threads, but block them
- lock.acquire()
- threads = []
- pool = handles[:] # shallow copy of the list, shared for pop()
- for i in range(NUM_THREADS):
- t = TestPool(lock, pool)
- t.start()
- threads.append(t)
- assert len(pool) == NUM_PAGES
- assert len(threads) == NUM_THREADS
-
- clock2 = time.time()
-
- # release lock to let the blocked threads run
- lock.release()
-
- # wait for threads to finish
- for t in threads:
- t.join()
-
- clock3 = time.time()
-
- # close handles
- for c in handles:
- c.close()
-
- clock4 = time.time()
- print "thread interface [pool]: %d pages: perform %5.2f secs, total %5.2f secs" % (NUM_PAGES, clock3 - clock2, clock4 - clock1)
-
- # print result
- print_result(handles)
-
-
-
-lock = RLock()
-if 1:
- test_multi()
- test_threads()
- test_threads(lock)
- test_thread_pool(lock)
-else:
- test_thread_pool(lock)
- test_threads(lock)
- test_threads()
- test_multi()
-
diff --git a/tests/test_post.py b/tests/test_post.py
deleted file mode 100644
index f0a8ad0..0000000
--- a/tests/test_post.py
+++ /dev/null
@@ -1,24 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-# vi:ts=4:et
-# $Id$
-
-import urllib
-import pycurl
-
-# simple
-pf = {'field1': 'value1'}
-
-# multiple fields
-pf = {'field1':'value1', 'field2':'value2 with blanks', 'field3':'value3'}
-
-# multiple fields with & in field
-pf = {'field1':'value1', 'field2':'value2 with blanks and & chars',
- 'field3':'value3'}
-
-c = pycurl.Curl()
-c.setopt(c.URL, 'http://pycurl.sourceforge.net/tests/testpostvars.php')
-c.setopt(c.POSTFIELDS, urllib.urlencode(pf))
-c.setopt(c.VERBOSE, 1)
-c.perform()
-c.close()
diff --git a/tests/test_post2.py b/tests/test_post2.py
deleted file mode 100644
index 74a6eca..0000000
--- a/tests/test_post2.py
+++ /dev/null
@@ -1,18 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-# vi:ts=4:et
-# $Id$
-
-import pycurl
-
-pf = [('field1', 'this is a test using httppost & stuff'),
- ('field2', (pycurl.FORM_FILE, 'test_post.py', pycurl.FORM_FILE, 'test_post2.py')),
- ('field3', (pycurl.FORM_CONTENTS, 'this is wei\000rd, but null-bytes are okay'))
- ]
-
-c = pycurl.Curl()
-c.setopt(c.URL, 'http://www.contactor.se/~dast/postit.cgi')
-c.setopt(c.HTTPPOST, pf)
-c.setopt(c.VERBOSE, 1)
-c.perform()
-c.close()
diff --git a/tests/test_post3.py b/tests/test_post3.py
deleted file mode 100644
index 617eba2..0000000
--- a/tests/test_post3.py
+++ /dev/null
@@ -1,32 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-# vi:ts=4:et
-# $Id$
-
-import urllib
-POSTSTRING = urllib.urlencode({'field1':'value1', 'field2':'value2 with blanks', 'field3':'value3'})
-
-class test:
-
- def __init__(self):
- self.finished = False
-
- def read_cb(self, size):
- assert len(POSTSTRING) <= size
- if not self.finished:
- self.finished = True
- return POSTSTRING
- else:
- # Nothing more to read
- return ""
-
-import pycurl
-c = pycurl.Curl()
-t = test()
-c.setopt(c.URL, 'http://pycurl.sourceforge.net/tests/testpostvars.php')
-c.setopt(c.POST, 1)
-c.setopt(c.POSTFIELDSIZE, len(POSTSTRING))
-c.setopt(c.READFUNCTION, t.read_cb)
-c.setopt(c.VERBOSE, 1)
-c.perform()
-c.close()
diff --git a/tests/test_reset.py b/tests/test_reset.py
deleted file mode 100644
index 1addcfe..0000000
--- a/tests/test_reset.py
+++ /dev/null
@@ -1,75 +0,0 @@
-#!/usr/bin/python
-
-import sys
-import pycurl
-
-saw_error = 1
-
-def main():
- global saw_error
-
- pycurl.global_init(pycurl.GLOBAL_DEFAULT)
-
- outf = file("/dev/null", "rb+")
- cm = pycurl.CurlMulti()
-
- # Set multi handle's options
- cm.setopt(pycurl.M_PIPELINING, 1)
-
- eh = pycurl.Curl()
-
- for x in range(1, 20):
-
- eh.setopt(pycurl.WRITEDATA, outf)
- eh.setopt(pycurl.URL, sys.argv[1])
- cm.add_handle(eh)
-
- while 1:
- ret, active_handles = cm.perform()
- if ret != pycurl.E_CALL_MULTI_PERFORM:
- break
-
- while active_handles:
- ret = cm.select(1.0)
- if ret == -1:
- continue
- while 1:
- ret, active_handles = cm.perform()
- if ret != pycurl.E_CALL_MULTI_PERFORM:
- break
-
- count, good, bad = cm.info_read()
-
- for h, en, em in bad:
- print "Transfer to %s failed with %d, %s\n" % \
- (h.getinfo(pycurl.EFFECTIVE_URL), en, em)
- raise RuntimeError
-
- for h in good:
- httpcode = h.getinfo(pycurl.RESPONSE_CODE)
- if httpcode != 200:
- print "Transfer to %s failed with code %d\n" %\
- (h.getinfo(pycurl.EFFECTIVE_URL), httpcode)
- raise RuntimeError
-
- else:
- print "Recd %d bytes from %s" % \
- (h.getinfo(pycurl.SIZE_DOWNLOAD),
- h.getinfo(pycurl.EFFECTIVE_URL))
-
- cm.remove_handle(eh)
- eh.reset()
-
- eh.close()
- cm.close()
- outf.close()
-
- pycurl.global_cleanup()
-
-
-if __name__ == '__main__':
- if len(sys.argv) != 2:
- print "Usage: %s <url>" % sys.argv[0]
- sys.exit(2)
- main()
-
diff --git a/tests/test_share.py b/tests/test_share.py
deleted file mode 100644
index 3332cda..0000000
--- a/tests/test_share.py
+++ /dev/null
@@ -1,34 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-# vi:ts=4:et
-# $Id$
-
-import sys
-import pycurl
-import threading
-
-print >>sys.stderr, 'Testing', pycurl.version
-
-
-class Test(threading.Thread):
-
- def __init__(self, share):
- threading.Thread.__init__(self)
- self.curl = pycurl.Curl()
- self.curl.setopt(pycurl.URL, 'http://curl.haxx.se')
- self.curl.setopt(pycurl.SHARE, share)
-
- def run(self):
- self.curl.perform()
- self.curl.close()
-
-s = pycurl.CurlShare()
-s.setopt(pycurl.SH_SHARE, pycurl.LOCK_DATA_COOKIE)
-s.setopt(pycurl.SH_SHARE, pycurl.LOCK_DATA_DNS)
-
-t1 = Test(s)
-t2 = Test(s)
-
-t1.start()
-t2.start()
-del s
diff --git a/tests/test_socketopen.py b/tests/test_socketopen.py
deleted file mode 100644
index d3f0a62..0000000
--- a/tests/test_socketopen.py
+++ /dev/null
@@ -1,17 +0,0 @@
-import pycurl
-import StringIO
-import socket
-
-def socketopen(family, socktype, protocol):
- print family, socktype, protocol
- s = socket.socket(family, socktype, protocol)
- s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
- return s
-
-sio = StringIO.StringIO()
-
-c = pycurl.Curl()
-c.setopt(pycurl.OPENSOCKETFUNCTION, socketopen)
-c.setopt(pycurl.URL, 'http://camvine.com')
-c.setopt(pycurl.WRITEFUNCTION, sio.write)
-c.perform()
diff --git a/tests/test_stringio.py b/tests/test_stringio.py
deleted file mode 100644
index 25e639b..0000000
--- a/tests/test_stringio.py
+++ /dev/null
@@ -1,25 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-# vi:ts=4:et
-# $Id$
-
-import sys
-try:
- from cStringIO import StringIO
-except ImportError:
- from StringIO import StringIO
-import pycurl
-
-url = "http://curl.haxx.se/dev/"
-
-print "Testing", pycurl.version
-
-body = StringIO()
-c = pycurl.Curl()
-c.setopt(c.URL, url)
-c.setopt(c.WRITEFUNCTION, body.write)
-c.perform()
-c.close()
-
-contents = body.getvalue()
-print contents
--
1.7.1
From f19bc32a8351b67ab3690b3ee0d4fe6843749a9b Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 13:36:38 -0500
Subject: [PATCH 063/149] Fix make test to run nosetests (closes #5)
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
Makefile | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/Makefile b/Makefile
index 9b2369d..9475250 100644
--- a/Makefile
+++ b/Makefile
@@ -7,6 +7,7 @@ SHELL = /bin/sh
PYTHON = python2.3
PYTHON = python
+NOSETESTS = nosetests
all build:
$(PYTHON) setup.py build
@@ -15,7 +16,8 @@ build-7.10.8:
$(PYTHON) setup.py build --curl-config=/home/hosts/localhost/packages/curl-7.10.8/bin/curl-config
test: build
- $(PYTHON) tests/test_internals.py -q
+ PYTHONPATH=$$(ls -d build/lib.*):$$PYTHONPATH \
+ $(NOSETESTS)
# (needs GNU binutils)
strip: build
--
1.7.1
From 61d61649b7687c85bdcef3da3650bcce03c4735d Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 04:25:47 -0500
Subject: [PATCH 064/149] First stab at travis configuration
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
.travis.yml | 11 +++++++++++
requirements-dev.txt | 1 +
2 files changed, 12 insertions(+), 0 deletions(-)
create mode 100644 .travis.yml
create mode 100644 requirements-dev.txt
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..2350dfb
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,11 @@
+language: python
+python:
+ - "2.5"
+ - "2.6"
+ - "2.7"
+install: >
+ pip install -r requirements-dev.txt --use-mirrors &&
+ sudo apt-get install vsftpd
+script: >
+ export PYCURL_VSFTPD_PATH=/usr/sbin/vsftpd &&
+ nosetests
diff --git a/requirements-dev.txt b/requirements-dev.txt
new file mode 100644
index 0000000..f3c7e8e
--- /dev/null
+++ b/requirements-dev.txt
@@ -0,0 +1 @@
+nose
--
1.7.1
From 8ceafd381dd7bca305686e33529a207b6544e2b4 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 04:37:47 -0500
Subject: [PATCH 065/149] Building the C module will help
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
.travis.yml | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 2350dfb..ae17929 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,5 +7,7 @@ install: >
pip install -r requirements-dev.txt --use-mirrors &&
sudo apt-get install vsftpd
script: >
+ make &&
+ export PYTHONPATH=build/lib.* &&
export PYCURL_VSFTPD_PATH=/usr/sbin/vsftpd &&
nosetests
--
1.7.1
From 1365d5ca2d70074d588cd23392862264494f0eeb Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 04:45:46 -0500
Subject: [PATCH 066/149] Go through more hoops (/bin/dash in play?)
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
.travis.yml | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index ae17929..4afdff9 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,6 +8,6 @@ install: >
sudo apt-get install vsftpd
script: >
make &&
- export PYTHONPATH=build/lib.* &&
+ export PYTHONPATH=$(ls -d build/lib.*) &&
export PYCURL_VSFTPD_PATH=/usr/sbin/vsftpd &&
nosetests
--
1.7.1
From 99896926cfdfc180f8c330cf2371c6cb023b4bac Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 04:50:21 -0500
Subject: [PATCH 067/149] Forgot about bottle
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
requirements-dev.txt | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/requirements-dev.txt b/requirements-dev.txt
index f3c7e8e..36b0b24 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1 +1,2 @@
+bottle
nose
--
1.7.1
From ce37bf646c42352895ba83c6bf7364012666c5c7 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 06:20:25 -0500
Subject: [PATCH 068/149] Python 2.5 needs simplejson
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
.travis.yml | 6 +++++-
requirements-dev-2.5.txt | 2 ++
2 files changed, 7 insertions(+), 1 deletions(-)
create mode 100644 requirements-dev-2.5.txt
diff --git a/.travis.yml b/.travis.yml
index 4afdff9..4268895 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,7 +4,11 @@ python:
- "2.6"
- "2.7"
install: >
- pip install -r requirements-dev.txt --use-mirrors &&
+ if test -e requirements-dev-$TRAVIS_PYTHON_VERSION.txt; then
+ pip install -r requirements-dev-$TRAVIS_PYTHON_VERSION.txt --use-mirrors
+ else
+ pip install -r requirements-dev.txt --use-mirrors
+ fi &&
sudo apt-get install vsftpd
script: >
make &&
diff --git a/requirements-dev-2.5.txt b/requirements-dev-2.5.txt
new file mode 100644
index 0000000..52e3460
--- /dev/null
+++ b/requirements-dev-2.5.txt
@@ -0,0 +1,2 @@
+-r requirements-dev.txt
+simplejson
--
1.7.1
From bc7eb4aad84f8249356c9d221fd5f404943f6699 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Thu, 7 Mar 2013 04:23:52 -0500
Subject: [PATCH 069/149] Expand readme (mostly borrowed from @Lispython's fork)
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
README | 13 --------
README.rst | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 93 insertions(+), 13 deletions(-)
delete mode 100644 README
create mode 100644 README.rst
diff --git a/README b/README
deleted file mode 100644
index 6b3e1d4..0000000
--- a/README
+++ /dev/null
@@ -1,13 +0,0 @@
-License
--------
-
-Copyright (C) 2001-2008 by Kjetil Jacobsen <kjetilja at gmail.com>
-Copyright (C) 2001-2008 by Markus F.X.J. Oberhumer <markus at oberhumer.com>
-
-All rights reserved.
-
-PycURL is dual licensed under the LGPL and an MIT/X derivative license
-based on the cURL license. A full copy of the LGPL license is included
-in the file COPYING. A full copy of the MIT/X derivative license is
-included in the file COPYING2. You can redistribute and/or modify PycURL
-according to the terms of either license.
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..3518d9d
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,93 @@
+PycURL: Python interface to libcurl
+====================================
+
+PycURL is a Python interface to `libcurl`_. PycURL can be used to fetch objects
+identified by a URL from a Python program, similar to the `urllib`_ Python module.
+PycURL is mature, very fast, and supports a lot of features.
+
+Overview
+--------
+
+- libcurl is a free and easy-to-use client-side URL transfer library, supporting
+ FTP, FTPS, HTTP, HTTPS, SCP, SFTP, TFTP, TELNET, DICT, LDAP, LDAPS, FILE, IMAP,
+ SMTP, POP3 and RTSP. libcurl supports SSL certificates, HTTP POST, HTTP PUT,
+ FTP uploading, HTTP form based upload, proxies, cookies, user+password
+ authentication (Basic, Digest, NTLM, Negotiate, Kerberos4), file transfer
+ resume, http proxy tunneling and more!
+
+- libcurl is highly portable, it builds and works identically on numerous
+ platforms, including Solaris, NetBSD, FreeBSD, OpenBSD, Darwin, HPUX, IRIX,
+ AIX, Tru64, Linux, UnixWare, HURD, Windows, Amiga, OS/2, BeOs, Mac OS X,
+ Ultrix, QNX, OpenVMS, RISC OS, Novell NetWare, DOS and more...
+
+- libcurl is `free`_, `thread-safe`_, `IPv6 compatible`_, `feature rich`_,
+ `well supported`_, `fast`_, `thoroughly documented`_ and is already used by
+ many known, big and successful `companies`_ and numerous `applications`_.
+
+.. _free: http://curl.haxx.se/docs/copyright.html
+.. _thread-safe: http://curl.haxx.se/libcurl/features.html#thread
+.. _`IPv6 compatible`: http://curl.haxx.se/libcurl/features.html#ipv6
+.. _`feature rich`: http://curl.haxx.se/libcurl/features.html#features
+.. _`well supported`: http://curl.haxx.se/libcurl/features.html#support
+.. _`fast`: http://curl.haxx.se/libcurl/features.html#fast
+.. _`thoroughly documented`: http://curl.haxx.se/libcurl/features.html#docs
+.. _companies: http://curl.haxx.se/docs/companies.html
+.. _applications: http://curl.haxx.se/libcurl/using/apps.html
+
+Installation
+------------
+
+You can install the most recent PycURL version using `easy_install`_::
+
+ easy_install pycurl
+
+or `pip`_::
+
+ pip install pycurl
+
+
+.. _easy_install: http://peak.telecommunity.com/DevCenter/EasyInstall
+.. _pip: http://pypi.python.org/pypi/pip
+
+Contribute
+----------
+
+For smaller changes:
+
+#. Fork `the repository`_ on Github.
+#. Create a branch off **master**.
+#. Make your changes.
+#. Write a test which shows that the bug was fixed or that the feature
+ works as expected.
+#. Send a pull request.
+
+For larger changes:
+
+#. Join the `mailing list`_.
+#. Discuss your proposal on the mailing list.
+#. When consensus is reached, implement it as described above.
+
+.. image:: https://api.travis-ci.org/p/pycurl.png
+ :target: https://travis-ci.org/p/pycurl
+
+License
+-------
+
+::
+
+ Copyright (C) 2001-2008 by Kjetil Jacobsen <kjetilja at gmail.com>
+ Copyright (C) 2001-2008 by Markus F.X.J. Oberhumer <markus at oberhumer.com>
+
+ All rights reserved.
+
+ PycURL is dual licensed under the LGPL and an MIT/X derivative license
+ based on the cURL license. A full copy of the LGPL license is included
+ in the file COPYING. A full copy of the MIT/X derivative license is
+ included in the file COPYING2. You can redistribute and/or modify PycURL
+ according to the terms of either license.
+
+.. _PycURL: http://pycurl.sourceforge.net/
+.. _libcurl: http://curl.haxx.se/libcurl/
+.. _urllib: http://docs.python.org/library/urllib.html
+.. _`the repository`: https://github.com/p/pycurl
+.. _`mailing list`: http://cool.haxx.se/mailman/listinfo/curl-and-python
--
1.7.1
From 0d6217a0204fe48fae9b6db0b5b73f2a71cdb90c Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Thu, 7 Mar 2013 04:30:23 -0500
Subject: [PATCH 070/149] Readme -> readme.rst elsewhere
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
MANIFEST.in | 2 +-
setup.py | 2 +-
tests/ftp_test.py | 6 +++---
tests/post_test.py | 4 ++--
4 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/MANIFEST.in b/MANIFEST.in
index 7d5aaf5..11ce1fe 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -8,7 +8,7 @@ include COPYING
include COPYING2
include INSTALL
include Makefile
-include README
+include README.rst
include TODO
include MANIFEST.in
include src/Makefile
diff --git a/setup.py b/setup.py
index 33704ef..235e4c9 100644
--- a/setup.py
+++ b/setup.py
@@ -166,7 +166,7 @@ def get_data_files():
else:
datadir = os.path.join("share", "doc", PACKAGE)
#
- files = ["ChangeLog", "COPYING", "COPYING2", "INSTALL", "README", "TODO",]
+ files = ["ChangeLog", "COPYING", "COPYING2", "INSTALL", "README.rst", "TODO",]
if files:
data_files.append((os.path.join(datadir), files))
files = glob.glob(os.path.join("doc", "*.html"))
diff --git a/tests/ftp_test.py b/tests/ftp_test.py
index fa2ef79..5ee380c 100644
--- a/tests/ftp_test.py
+++ b/tests/ftp_test.py
@@ -26,7 +26,7 @@ class FtpTest(unittest.TestCase):
self.curl.perform()
result = sio.getvalue()
- assert 'README' in result
+ assert 'README.rst' in result
assert 'INSTALL' in result
# XXX this test needs to be fixed
@@ -38,7 +38,7 @@ class FtpTest(unittest.TestCase):
self.curl.perform()
result = sio.getvalue()
- assert 'README' not in result
+ assert 'README.rst' not in result
assert 'ftp_test.py' in result
def test_epsv(self):
@@ -49,5 +49,5 @@ class FtpTest(unittest.TestCase):
self.curl.perform()
result = sio.getvalue()
- assert 'README' in result
+ assert 'README.rst' in result
assert 'INSTALL' in result
diff --git a/tests/post_test.py b/tests/post_test.py
index e8b0675..6f9d8d4 100644
--- a/tests/post_test.py
+++ b/tests/post_test.py
@@ -81,7 +81,7 @@ class PostTest(unittest.TestCase):
self.check_post(send, expect, 'http://localhost:8380/postfields')
def test_post_file(self):
- path = os.path.join(os.path.dirname(__file__), '..', 'README')
+ path = os.path.join(os.path.dirname(__file__), '..', 'README.rst')
with open(path) as f:
contents = f.read()
send = [
@@ -90,7 +90,7 @@ class PostTest(unittest.TestCase):
]
expect = [{
'name': 'field2',
- 'filename': 'README',
+ 'filename': 'README.rst',
'data': contents,
}]
self.check_post(send, expect, 'http://localhost:8380/files')
--
1.7.1
From 1bcb3684a9ba30411c675ddc2e75e9a5a5def311 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 04:50:34 -0500
Subject: [PATCH 071/149] Python 2.5 compatibility: with statement
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/default_write_function_test.py | 2 ++
tests/post_test.py | 2 ++
tests/write_to_file_test.py | 2 ++
3 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/tests/default_write_function_test.py b/tests/default_write_function_test.py
index c0d256b..1d31e97 100644
--- a/tests/default_write_function_test.py
+++ b/tests/default_write_function_test.py
@@ -2,6 +2,8 @@
# -*- coding: iso-8859-1 -*-
# vi:ts=4:et
+from __future__ import with_statement
+
import unittest
import pycurl
import sys
diff --git a/tests/post_test.py b/tests/post_test.py
index 6f9d8d4..804104e 100644
--- a/tests/post_test.py
+++ b/tests/post_test.py
@@ -2,6 +2,8 @@
# -*- coding: iso-8859-1 -*-
# vi:ts=4:et
+from __future__ import with_statement
+
import os.path
import pycurl
import unittest
diff --git a/tests/write_to_file_test.py b/tests/write_to_file_test.py
index 67c9c63..c3c8822 100644
--- a/tests/write_to_file_test.py
+++ b/tests/write_to_file_test.py
@@ -2,6 +2,8 @@
# -*- coding: iso-8859-1 -*-
# vi:ts=4:et
+from __future__ import with_statement
+
import unittest
import pycurl
import tempfile
--
1.7.1
From 05d4fbe859d886ec0f0d104b5114a0f416021097 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 06:10:22 -0500
Subject: [PATCH 072/149] Python 2.5 compatibility: json/simplejson
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/app.py | 5 ++++-
tests/post_test.py | 5 ++++-
tests/post_with_read_callback_test.py | 5 ++++-
3 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/tests/app.py b/tests/app.py
index a83e628..eb10668 100644
--- a/tests/app.py
+++ b/tests/app.py
@@ -1,5 +1,8 @@
import bottle
-import json
+try:
+ import json
+except ImportError:
+ import simplejson as json
app = bottle.Bottle()
diff --git a/tests/post_test.py b/tests/post_test.py
index 804104e..a36a677 100644
--- a/tests/post_test.py
+++ b/tests/post_test.py
@@ -8,7 +8,10 @@ import os.path
import pycurl
import unittest
import io
-import json
+try:
+ import json
+except ImportError:
+ import simplejson as json
try:
import urllib.parse as urllib_parse
except ImportError:
diff --git a/tests/post_with_read_callback_test.py b/tests/post_with_read_callback_test.py
index a09e83a..4d8f261 100644
--- a/tests/post_with_read_callback_test.py
+++ b/tests/post_with_read_callback_test.py
@@ -6,7 +6,10 @@ import os.path
import pycurl
import unittest
import io
-import json
+try:
+ import json
+except ImportError:
+ import simplejson as json
try:
import urllib.parse as urllib_parse
except ImportError:
--
1.7.1
From f29edfdd24ee3524a2674ba149c8a8d25741263c Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 06:18:02 -0500
Subject: [PATCH 073/149] Python 2.5 compatibility: except as
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/runwsgi.py | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/tests/runwsgi.py b/tests/runwsgi.py
index 5217a3f..8a978ec 100644
--- a/tests/runwsgi.py
+++ b/tests/runwsgi.py
@@ -1,5 +1,6 @@
# Run a WSGI application in a daemon thread
+import sys
import bottle
import threading
import socket
@@ -21,7 +22,8 @@ def wait_for_network_service(netloc, check_interval, num_attempts):
for i in range(num_attempts):
try:
conn = socket.create_connection(netloc, check_interval)
- except socket.error as e:
+ except socket.error:
+ e = sys.exc_info()[1]
_time.sleep(check_interval)
else:
conn.close()
--
1.7.1
From 28f459a96fcc6c74afa50b7b3c01a2dd75bd59f8 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 06:23:08 -0500
Subject: [PATCH 074/149] Delete unused io imports
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/default_write_function_test.py | 1 -
tests/header_function_test.py | 1 -
tests/post_test.py | 1 -
tests/post_with_read_callback_test.py | 1 -
tests/write_to_stringio_test.py | 1 -
5 files changed, 0 insertions(+), 5 deletions(-)
diff --git a/tests/default_write_function_test.py b/tests/default_write_function_test.py
index 1d31e97..27a3d04 100644
--- a/tests/default_write_function_test.py
+++ b/tests/default_write_function_test.py
@@ -8,7 +8,6 @@ import unittest
import pycurl
import sys
import tempfile
-import io
import os
from . import app
diff --git a/tests/header_function_test.py b/tests/header_function_test.py
index bfe7173..00080ba 100644
--- a/tests/header_function_test.py
+++ b/tests/header_function_test.py
@@ -4,7 +4,6 @@
import pycurl
import unittest
-import io
import time as _time
from . import app
diff --git a/tests/post_test.py b/tests/post_test.py
index a36a677..c11c3c8 100644
--- a/tests/post_test.py
+++ b/tests/post_test.py
@@ -7,7 +7,6 @@ from __future__ import with_statement
import os.path
import pycurl
import unittest
-import io
try:
import json
except ImportError:
diff --git a/tests/post_with_read_callback_test.py b/tests/post_with_read_callback_test.py
index 4d8f261..f0776ea 100644
--- a/tests/post_with_read_callback_test.py
+++ b/tests/post_with_read_callback_test.py
@@ -5,7 +5,6 @@
import os.path
import pycurl
import unittest
-import io
try:
import json
except ImportError:
diff --git a/tests/write_to_stringio_test.py b/tests/write_to_stringio_test.py
index 018800d..e9ab0c7 100644
--- a/tests/write_to_stringio_test.py
+++ b/tests/write_to_stringio_test.py
@@ -4,7 +4,6 @@
import pycurl
import unittest
-import io
from . import app
from . import runwsgi
--
1.7.1
From 8e2ee4ec10741ae7373f3c490df6696267871c12 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 04:54:35 -0500
Subject: [PATCH 075/149] Ignore fsync of stdout failures on travis
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/default_write_function_test.py | 13 +++++++++++--
1 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/tests/default_write_function_test.py b/tests/default_write_function_test.py
index 27a3d04..1c8ec16 100644
--- a/tests/default_write_function_test.py
+++ b/tests/default_write_function_test.py
@@ -18,6 +18,15 @@ setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380))
STDOUT_FD_NUM = 1
+def try_fsync(fd):
+ try:
+ os.fsync(fd)
+ except OSError:
+ # On travis:
+ # OSError: [Errno 22] Invalid argument
+ # ignore
+ pass
+
class DefaultWriteFunctionTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
@@ -38,7 +47,7 @@ class DefaultWriteFunctionTest(unittest.TestCase):
# If this flush is not done, stdout output bleeds into the next test
# that is executed (without nose output capture)
sys.stdout.flush()
- os.fsync(STDOUT_FD_NUM)
+ try_fsync(STDOUT_FD_NUM)
# I have a really hard time getting this to work with nose output capture
def skip_perform_get_with_default_write_function(self):
@@ -67,7 +76,7 @@ class DefaultWriteFunctionTest(unittest.TestCase):
self.curl.perform()
sys.stdout.flush()
finally:
- os.fsync(STDOUT_FD_NUM)
+ try_fsync(STDOUT_FD_NUM)
os.dup2(saved_stdout_fd, STDOUT_FD_NUM)
os.close(saved_stdout_fd)
#os.dup2(100, 1)
--
1.7.1
From e7da1d864d955f21f31bdf880ed903abbee7c2ba Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 04:58:42 -0500
Subject: [PATCH 076/149] My vps says timeout might be -1 there
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/multi_socket_select_test.py | 5 +++--
1 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/tests/multi_socket_select_test.py b/tests/multi_socket_select_test.py
index 0c472cf..6db8b44 100644
--- a/tests/multi_socket_select_test.py
+++ b/tests/multi_socket_select_test.py
@@ -73,8 +73,9 @@ class MultiSocketSelectTest(unittest.TestCase):
timeout = m.timeout()
-
- while True:
+ # timeout might be -1, indicating that all work is done
+ # XXX make sure there is always work to be done here?
+ while timeout >= 0:
(rr, wr, er) = select.select(sockets,sockets,sockets,timeout/1000.0)
socketSet = set(rr+wr+er)
if socketSet:
--
1.7.1
From ee685a967f67e26ae32ec0c1d34fbad461fce2e7 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 06:14:57 -0500
Subject: [PATCH 077/149] Show pycurl versions in package setup
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/__init__.py | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/tests/__init__.py b/tests/__init__.py
index e69de29..c1ff976 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -0,0 +1,4 @@
+import pycurl
+
+def setup_package():
+ print('Testing %s' % pycurl.version)
--
1.7.1
From 359fc6e31a21620ea564288ae43ae23db35d1b2e Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 06:07:19 -0500
Subject: [PATCH 078/149] Show what the entries are if assertion fails
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/debug_test.py | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/tests/debug_test.py b/tests/debug_test.py
index 8005239..d74bed9 100644
--- a/tests/debug_test.py
+++ b/tests/debug_test.py
@@ -46,4 +46,5 @@ class DebugTest(unittest.TestCase):
for t, b in self.debug_entries:
if t == wanted_t and wanted_b in b:
return
- assert False, "%d: %s not found in debug entries" % (wanted_t, wanted_b)
+ assert False, "%d: %s not found in debug entries\nEntries are:\n%s" % \
+ (wanted_t, wanted_b, repr(self.debug_entries))
--
1.7.1
From 4c65d037fce349811d4954db137d0b3d5b3abe85 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 06:09:22 -0500
Subject: [PATCH 079/149] More informative failure message
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/multi_test.py | 5 ++++-
1 files changed, 4 insertions(+), 1 deletions(-)
diff --git a/tests/multi_test.py b/tests/multi_test.py
index d9c6174..8701649 100644
--- a/tests/multi_test.py
+++ b/tests/multi_test.py
@@ -214,7 +214,10 @@ class MultiTest(unittest.TestCase):
self.assertEqual('success', m.handles[0].body.getvalue())
self.assertEqual(200, m.handles[0].http_code)
# bottle generated response body
- assert 'Error 403: Forbidden' in m.handles[1].body.getvalue()
+ body = m.handles[1].body.getvalue()
+ search = 'Error 403: Forbidden'
+ if search not in body:
+ assert False, "'%s' not found in body:\n%s" % (search, body)
self.assertEqual(403, m.handles[1].http_code)
# bottle generated response body
self.assertEqual('', m.handles[2].body.getvalue())
--
1.7.1
From 831b7a919d47b55e787c8af457b6ec815fca2035 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 06:34:39 -0500
Subject: [PATCH 080/149] Return complete response bodies for 403 and 404 responses as it looks like exact wording varies between bottle versions
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/app.py | 4 ++--
tests/multi_test.py | 9 +++------
2 files changed, 5 insertions(+), 8 deletions(-)
diff --git a/tests/app.py b/tests/app.py
index eb10668..9b56ace 100644
--- a/tests/app.py
+++ b/tests/app.py
@@ -12,11 +12,11 @@ def ok():
@app.route('/status/403')
def forbidden():
- bottle.abort(403, 'forbidden')
+ return bottle.HTTPResponse('forbidden', 403)
@app.route('/status/404')
def not_found():
- bottle.abort(404, 'not found')
+ return bottle.HTTPResponse('not found', 404)
@app.route('/postfields', method='post')
def postfields():
diff --git a/tests/multi_test.py b/tests/multi_test.py
index 8701649..d540413 100644
--- a/tests/multi_test.py
+++ b/tests/multi_test.py
@@ -153,10 +153,10 @@ class MultiTest(unittest.TestCase):
self.assertEqual('success', m.handles[0].body.getvalue())
self.assertEqual(200, m.handles[0].http_code)
# bottle generated response body
- assert 'Error 403: Forbidden' in m.handles[1].body.getvalue()
+ self.assertEqual('forbidden', m.handles[1].body.getvalue())
self.assertEqual(403, m.handles[1].http_code)
# bottle generated response body
- assert 'Error 404: Not Found' in m.handles[2].body.getvalue()
+ self.assertEqual('not found', m.handles[2].body.getvalue())
self.assertEqual(404, m.handles[2].http_code)
def check_adding_closed_handle(self, close_fn):
@@ -214,10 +214,7 @@ class MultiTest(unittest.TestCase):
self.assertEqual('success', m.handles[0].body.getvalue())
self.assertEqual(200, m.handles[0].http_code)
# bottle generated response body
- body = m.handles[1].body.getvalue()
- search = 'Error 403: Forbidden'
- if search not in body:
- assert False, "'%s' not found in body:\n%s" % (search, body)
+ self.assertEqual('forbidden', m.handles[1].body.getvalue())
self.assertEqual(403, m.handles[1].http_code)
# bottle generated response body
self.assertEqual('', m.handles[2].body.getvalue())
--
1.7.1
From 6730160a7ede5c1bb490836cb42269d06a497dbe Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 22:15:47 -0500
Subject: [PATCH 081/149] Debug messages originated by curl 7.22 are different
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/debug_test.py | 6 +++++-
1 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/tests/debug_test.py b/tests/debug_test.py
index d74bed9..70e121c 100644
--- a/tests/debug_test.py
+++ b/tests/debug_test.py
@@ -32,7 +32,11 @@ class DebugTest(unittest.TestCase):
# Some checks with no particular intent
self.check(0, 'About to connect')
- self.check(0, 'Connected to localhost')
+ version = map(int, pycurl.version_info()[1].split('.'))
+ if version[0] < 7 or version[0] == 7 and version[1] <= 22:
+ self.check(0, 'connected')
+ else:
+ self.check(0, 'Connected to localhost')
self.check(0, 'port 8380')
# request
self.check(2, 'GET /success HTTP/1.1')
--
1.7.1
From 283ebc888e8016f575457db70a81eb46f219467c Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 22:17:48 -0500
Subject: [PATCH 082/149] Times in http headers are in UTC
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/header_function_test.py | 3 ++-
1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/tests/header_function_test.py b/tests/header_function_test.py
index 00080ba..7ca564d 100644
--- a/tests/header_function_test.py
+++ b/tests/header_function_test.py
@@ -34,7 +34,8 @@ class HeaderFunctionTest(unittest.TestCase):
assert len(self.header_lines) > 0
self.assertEqual("HTTP/1.0 200 OK\r\n", self.header_lines[0])
# day of week
- todays_day = _time.strftime('%a')
+ # important: must be in utc
+ todays_day = _time.strftime('%a', _time.gmtime())
# Date: Sun, 03 Mar 2013 05:38:12 GMT\r\n
self.check('Date: %s' % todays_day)
# Server: WSGIServer/0.1 Python/2.7.3\r\n
--
1.7.1
From 0034724dd8ec8526f361c067d72e87c930f6eeb3 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 22:26:19 -0500
Subject: [PATCH 083/149] Create a generalized function for curl version comparisons
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/util.py | 15 +++++++++++++++
tests/version_comparison_test.py | 15 +++++++++++++++
2 files changed, 30 insertions(+), 0 deletions(-)
create mode 100644 tests/version_comparison_test.py
diff --git a/tests/util.py b/tests/util.py
index 891da44..46ac59f 100644
--- a/tests/util.py
+++ b/tests/util.py
@@ -3,6 +3,7 @@
# $Id$
import os, sys
+import pycurl
try:
from cStringIO import StringIO
@@ -12,6 +13,20 @@ except ImportError:
except ImportError:
from io import StringIO
+def version_less_than_spec(version_tuple, spec_tuple):
+ # spec_tuple may have 2 elements, expect version_tuple to have 3 elements
+ assert len(version_tuple) >= len(spec_tuple)
+ for i in range(len(spec_tuple)):
+ if version_tuple[i] < spec_tuple[i]:
+ return True
+ if version_tuple[i] > spec_tuple[i]:
+ return False
+ return False
+
+def pycurl_version_less_than(spec_tuple):
+ version = map(int, pycurl.version_info()[1].split('.'))
+ return version_less_than_spec(version, spec_tuple)
+
#
# prepare sys.path in case we are still in the build directory
# see also: distutils/command/build.py (build_platlib)
diff --git a/tests/version_comparison_test.py b/tests/version_comparison_test.py
new file mode 100644
index 0000000..80e780c
--- /dev/null
+++ b/tests/version_comparison_test.py
@@ -0,0 +1,15 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+import unittest
+
+from . import util
+
+class VersionComparisonTest(unittest.TestCase):
+ def test_comparison(self):
+ assert util.version_less_than_spec((7, 22, 0), (7, 23, 0))
+ assert util.version_less_than_spec((7, 22, 0), (7, 23))
+ assert util.version_less_than_spec((7, 22, 0), (7, 22, 1))
+ assert not util.version_less_than_spec((7, 22, 0), (7, 22, 0))
+ assert not util.version_less_than_spec((7, 22, 0), (7, 22))
--
1.7.1
From d794edf43a119d4f600d3be8536c22419c9f4abb Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 22:27:25 -0500
Subject: [PATCH 084/149] Use version comparison helper in debug test
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/debug_test.py | 3 +--
tests/util.py | 4 ++--
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/tests/debug_test.py b/tests/debug_test.py
index 70e121c..e81d653 100644
--- a/tests/debug_test.py
+++ b/tests/debug_test.py
@@ -32,8 +32,7 @@ class DebugTest(unittest.TestCase):
# Some checks with no particular intent
self.check(0, 'About to connect')
- version = map(int, pycurl.version_info()[1].split('.'))
- if version[0] < 7 or version[0] == 7 and version[1] <= 22:
+ if util.pycurl_version_less_than(7, 24):
self.check(0, 'connected')
else:
self.check(0, 'Connected to localhost')
diff --git a/tests/util.py b/tests/util.py
index 46ac59f..b8e22ec 100644
--- a/tests/util.py
+++ b/tests/util.py
@@ -23,9 +23,9 @@ def version_less_than_spec(version_tuple, spec_tuple):
return False
return False
-def pycurl_version_less_than(spec_tuple):
+def pycurl_version_less_than(*spec):
version = map(int, pycurl.version_info()[1].split('.'))
- return version_less_than_spec(version, spec_tuple)
+ return version_less_than_spec(version, spec)
#
# prepare sys.path in case we are still in the build directory
--
1.7.1
From c1143004a0f27190fc4aec1228396db992f5c5de Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 6 Mar 2013 22:30:44 -0500
Subject: [PATCH 085/149] libcurl 7.23.0 produces different results
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/multi_timer_test.py | 7 +++++--
1 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/tests/multi_timer_test.py b/tests/multi_timer_test.py
index e961780..c4b3df4 100644
--- a/tests/multi_timer_test.py
+++ b/tests/multi_timer_test.py
@@ -77,8 +77,11 @@ class MultiSocketTest(unittest.TestCase):
self.assertEqual(200, c.http_code)
assert len(timers) > 0
- assert timers[0] > 0
- self.assertEqual(-1, timers[-1])
+ # libcurl 7.23.0 produces a 0 timer
+ assert timers[0] >= 0
+ # this assertion does not appear to hold on older libcurls
+ if not util.pycurl_version_less_than(7, 24):
+ self.assertEqual(-1, timers[-1])
# close handles
for c in m.handles:
--
1.7.1
From abc0199b4b2517a82c492cd3f64f10883a5ec257 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Thu, 7 Mar 2013 00:16:21 -0500
Subject: [PATCH 086/149] Change memleak test to use id() rather than regexp match of addresses (closes #12)
This commit also fixes the test to check objects that GC tracks
rather than the object type repeatedly.
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/memleak_test.py | 16 ++++++----------
1 files changed, 6 insertions(+), 10 deletions(-)
diff --git a/tests/memleak_test.py b/tests/memleak_test.py
index 6e9f76c..1b1bbd5 100644
--- a/tests/memleak_test.py
+++ b/tests/memleak_test.py
@@ -5,12 +5,9 @@
import pycurl
import unittest
import gc
-import re
class MemleakTest(unittest.TestCase):
def test_collection(self):
- regexp = re.compile(r'at (0x\d+)')
-
gc.collect()
flags = gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE
# python 3 has no DEBUG_OBJECTS
@@ -31,12 +28,10 @@ class MemleakTest(unittest.TestCase):
multi.add_handle(curl)
t.append(curl)
- match = regexp.search(repr(curl))
- assert match is not None
- searches.append(match.group(1))
- match = regexp.search(repr(multi))
- assert match
- searches.append(match.group(1))
+ c_id = id(curl)
+ searches.append(c_id)
+ m_id = id(multi)
+ searches.append(m_id)
#print("Tracked objects:", len(gc.get_objects()))
@@ -56,4 +51,5 @@ class MemleakTest(unittest.TestCase):
objects = gc.get_objects()
for search in searches:
- assert 'at %s' % search not in repr(object)
+ for object in objects:
+ assert search != id(object)
--
1.7.1
From 14dd7fdbe5697f7e7349cbe44258f41f74455fc0 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Mon, 11 Mar 2013 22:19:14 -0400
Subject: [PATCH 087/149] More informative exception message when vsftpd is missing or not in PATH (closes #6)
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/procmgr.py | 12 +++++++++++-
1 files changed, 11 insertions(+), 1 deletions(-)
diff --git a/tests/procmgr.py b/tests/procmgr.py
index 8d5c0cc..ce08da9 100644
--- a/tests/procmgr.py
+++ b/tests/procmgr.py
@@ -1,6 +1,7 @@
import threading
import subprocess
import os
+import sys
import signal
from . import runwsgi
@@ -53,7 +54,16 @@ def vsftpd_setup():
]
setup_module = start_setup(cmd)
def do_setup_module():
- setup_module()
+ try:
+ setup_module()
+ except OSError:
+ import errno
+ e = sys.exc_info()[1]
+ if e.errno == errno.ENOENT:
+ msg = "Tried to execute `%s`\nTry specifying path to vsftpd via PYCURL_VSFTPD_PATH environment variable\n" % vsftpd_path
+ raise OSError(e.errno, e.strerror + "\n" + msg)
+ else:
+ raise
ok = runwsgi.wait_for_network_service(('127.0.0.1', 8321), 0.1, 10)
if not ok:
import warnings
--
1.7.1
From 1505197b9eb8d5ccbc3d039866e87d522455d9d0 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Mon, 11 Mar 2013 22:22:02 -0400
Subject: [PATCH 088/149] Delete -1 timer assertion from multi timer test (closes #19)
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/multi_timer_test.py | 6 ++++--
1 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/tests/multi_timer_test.py b/tests/multi_timer_test.py
index c4b3df4..ff856d7 100644
--- a/tests/multi_timer_test.py
+++ b/tests/multi_timer_test.py
@@ -80,8 +80,10 @@ class MultiSocketTest(unittest.TestCase):
# libcurl 7.23.0 produces a 0 timer
assert timers[0] >= 0
# this assertion does not appear to hold on older libcurls
- if not util.pycurl_version_less_than(7, 24):
- self.assertEqual(-1, timers[-1])
+ # or apparently on any linuxes, see
+ # https://github.com/p/pycurl/issues/19
+ #if not util.pycurl_version_less_than(7, 24):
+ # self.assertEqual(-1, timers[-1])
# close handles
for c in m.handles:
--
1.7.1
From 041b4666519f3fa2594f5ee919e62006cb8ae2f1 Mon Sep 17 00:00:00 2001
From: Kamil Dudka <kdudka@redhat.com>
Date: Mon, 11 Mar 2013 14:00:04 +0100
Subject: [PATCH 089/149] remove .cvsignore files, add *.pyc and *.pyo to .gitignore
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
.gitignore | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
index 796b96d..899daba 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
+*.pyc
+*.pyo
/build
--
1.7.1
From 3dc27e280021512e84cd77f68c7ff3d4e5cb2ae0 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Mon, 11 Mar 2013 22:40:12 -0400
Subject: [PATCH 090/149] Readd cvsignore contents and empty directories
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
.gitignore | 6 ++++++
1 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
index 899daba..c873e32 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,9 @@
*.pyc
*.pyo
+/MANIFEST
/build
+/dist
+/www/htdocs/download/*.bz2
+/www/htdocs/download/*.exe
+/www/htdocs/download/*.gz
+/www/upload/*
--
1.7.1
From d6c800cdc963e349921cae8ac10df9f9f23cac79 Mon Sep 17 00:00:00 2001
From: Kamil Dudka <kdudka@redhat.com>
Date: Mon, 11 Mar 2013 14:03:06 +0100
Subject: [PATCH 091/149] vsftpd.conf: add background=no to allow for proper shutdown
When the "background" directive in the vsftpd.conf configuration file is
set to "YES", the vsftpd startup script forks, creating a child process
(the vsftpd daemon) which immediately sends the SIGUSR1 signal to its
parent process, which exits upon receiving it. The teardown routine in
procmgr.py would then send the SIGTERM signal to a process that does not
exist anymore.
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/vsftpd.conf | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/tests/vsftpd.conf b/tests/vsftpd.conf
index 0abb39f..b4e4972 100644
--- a/tests/vsftpd.conf
+++ b/tests/vsftpd.conf
@@ -1,5 +1,6 @@
anon_world_readable_only=yes
anonymous_enable=yes
+background=no
# currently we only list files
download_enable=no
listen=yes
--
1.7.1
From 8acdaaf5c311038a5dd7fcd81f13c0bb984b66c7 Mon Sep 17 00:00:00 2001
From: Kamil Dudka <kdudka@redhat.com>
Date: Tue, 26 Feb 2013 14:49:47 +0100
Subject: [PATCH 092/149] pycurl.c: eliminate duplicated code in util_write_callback()
Suggested by Zdenek Pavlas <https://bugzilla.redhat.com/857875#c8>.
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
src/pycurl.c | 10 +---------
1 files changed, 1 insertions(+), 9 deletions(-)
diff --git a/src/pycurl.c b/src/pycurl.c
index 094bc60..f701543 100644
--- a/src/pycurl.c
+++ b/src/pycurl.c
@@ -1082,15 +1082,7 @@ util_write_callback(int flags, char *ptr, size_t size, size_t nmemb, void *strea
if (result == Py_None) {
ret = total_size; /* None means success */
}
- else if (PyInt_Check(result)) {
- long obj_size = PyInt_AsLong(result);
- if (obj_size < 0 || obj_size > total_size) {
- PyErr_Format(ErrorObject, "invalid return value for write callback %ld %ld", (long)obj_size, (long)total_size);
- goto verbose_error;
- }
- ret = (size_t) obj_size; /* success */
- }
- else if (PyLong_Check(result)) {
+ else if (PyInt_Check(result) || PyLong_Check(result)) {
long obj_size = PyLong_AsLong(result);
if (obj_size < 0 || obj_size > total_size) {
PyErr_Format(ErrorObject, "invalid return value for write callback %ld %ld", (long)obj_size, (long)total_size);
--
1.7.1
From b55c2cab56733a86b4ebc5ccccdf8c5530bca85a Mon Sep 17 00:00:00 2001
From: Kamil Dudka <kdudka@redhat.com>
Date: Tue, 26 Feb 2013 16:58:55 +0100
Subject: [PATCH 093/149] pycurl.c: allow to return -1 from write callback
... to abort the transfer and WRITEFUNC_PAUSE to pause the transfer
Reported By: Zdenek Pavlas
Bug: https://bugzilla.redhat.com/857875
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
src/pycurl.c | 11 +++++------
1 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/src/pycurl.c b/src/pycurl.c
index f701543..a30c339 100644
--- a/src/pycurl.c
+++ b/src/pycurl.c
@@ -1083,12 +1083,8 @@ util_write_callback(int flags, char *ptr, size_t size, size_t nmemb, void *strea
ret = total_size; /* None means success */
}
else if (PyInt_Check(result) || PyLong_Check(result)) {
- long obj_size = PyLong_AsLong(result);
- if (obj_size < 0 || obj_size > total_size) {
- PyErr_Format(ErrorObject, "invalid return value for write callback %ld %ld", (long)obj_size, (long)total_size);
- goto verbose_error;
- }
- ret = (size_t) obj_size; /* success */
+ /* if the cast to long fails, PyLong_AsLong() returns -1L */
+ ret = (size_t) PyLong_AsLong(result);
}
else {
PyErr_SetString(ErrorObject, "write callback must return int or None");
@@ -3509,6 +3505,9 @@ initpycurl(void)
/* Abort curl_read_callback(). */
insint_c(d, "READFUNC_ABORT", CURL_READFUNC_ABORT);
+ /* Pause curl_write_callback(). */
+ insint_c(d, "WRITEFUNC_PAUSE", CURL_WRITEFUNC_PAUSE);
+
/* constants for ioctl callback return values */
insint_c(d, "IOE_OK", CURLIOE_OK);
insint_c(d, "IOE_UNKNOWNCMD", CURLIOE_UNKNOWNCMD);
--
1.7.1
From 32664e552084fad471de98f219f58c52c95653f4 Mon Sep 17 00:00:00 2001
From: Kamil Dudka <kdudka@redhat.com>
Date: Wed, 6 Mar 2013 14:38:01 +0100
Subject: [PATCH 094/149] write_abort_test.py: test returning -1 from write callback
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/write_abort_test.py | 35 +++++++++++++++++++++++++++++++++++
1 files changed, 35 insertions(+), 0 deletions(-)
create mode 100755 tests/write_abort_test.py
diff --git a/tests/write_abort_test.py b/tests/write_abort_test.py
new file mode 100755
index 0000000..73e8245
--- /dev/null
+++ b/tests/write_abort_test.py
@@ -0,0 +1,35 @@
+#!/usr/bin/python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+import os.path
+import pycurl
+import sys
+import unittest
+
+class WriteAbortTest(unittest.TestCase):
+ def setUp(self):
+ pycurl.global_init(pycurl.GLOBAL_DEFAULT)
+
+ def tearDown(self):
+ pycurl.global_cleanup()
+
+ def test_write_abort(self):
+ def write_cb(_):
+ # this should cause pycurl.WRITEFUNCTION (without any range errors)
+ return -1
+
+ # download the script itself through the file:// protocol into write_cb
+ c = pycurl.Curl()
+ c.setopt(pycurl.URL, 'file://' + os.path.abspath(sys.argv[0]))
+ c.setopt(pycurl.WRITEFUNCTION, write_cb)
+ try:
+ c.perform()
+ except pycurl.error, (err, msg):
+ # we expect pycurl.E_WRITE_ERROR as the response
+ assert pycurl.E_WRITE_ERROR == err
+
+ # no additional errors should be reported
+ assert not hasattr(sys, 'last_value')
+
+ c.close()
--
1.7.1
From 808c5b785ccd2f3bcca15146ae998a8609229b38 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Mon, 11 Mar 2013 23:48:10 -0400
Subject: [PATCH 095/149] setup/teardown consistent with other tests
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/write_abort_test.py | 13 +++++--------
1 files changed, 5 insertions(+), 8 deletions(-)
diff --git a/tests/write_abort_test.py b/tests/write_abort_test.py
index 73e8245..f720882 100755
--- a/tests/write_abort_test.py
+++ b/tests/write_abort_test.py
@@ -9,10 +9,10 @@ import unittest
class WriteAbortTest(unittest.TestCase):
def setUp(self):
- pycurl.global_init(pycurl.GLOBAL_DEFAULT)
+ self.curl = pycurl.Curl()
def tearDown(self):
- pycurl.global_cleanup()
+ self.curl.close()
def test_write_abort(self):
def write_cb(_):
@@ -20,16 +20,13 @@ class WriteAbortTest(unittest.TestCase):
return -1
# download the script itself through the file:// protocol into write_cb
- c = pycurl.Curl()
- c.setopt(pycurl.URL, 'file://' + os.path.abspath(sys.argv[0]))
- c.setopt(pycurl.WRITEFUNCTION, write_cb)
+ self.curl.setopt(pycurl.URL, 'file://' + os.path.abspath(sys.argv[0]))
+ self.curl.setopt(pycurl.WRITEFUNCTION, write_cb)
try:
- c.perform()
+ self.curl.perform()
except pycurl.error, (err, msg):
# we expect pycurl.E_WRITE_ERROR as the response
assert pycurl.E_WRITE_ERROR == err
# no additional errors should be reported
assert not hasattr(sys, 'last_value')
-
- c.close()
--
1.7.1
From c662b504c202ca434d723de3984c12a35358e9f8 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Mon, 11 Mar 2013 23:48:41 -0400
Subject: [PATCH 096/149] Correct shebang
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/write_abort_test.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/tests/write_abort_test.py b/tests/write_abort_test.py
index f720882..3272961 100755
--- a/tests/write_abort_test.py
+++ b/tests/write_abort_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#! /usr/bin/env python
# -*- coding: iso-8859-1 -*-
# vi:ts=4:et
--
1.7.1
From 43d385231293a22fb65490db8348c9a18592c7b0 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Mon, 11 Mar 2013 23:56:36 -0400
Subject: [PATCH 097/149] Check that bogus return values from write callback are correctly handled (still)
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/write_cb_bogus_test.py | 44 ++++++++++++++++++++++++++++++++++++++++++
1 files changed, 44 insertions(+), 0 deletions(-)
create mode 100644 tests/write_cb_bogus_test.py
diff --git a/tests/write_cb_bogus_test.py b/tests/write_cb_bogus_test.py
new file mode 100644
index 0000000..4bec2ad
--- /dev/null
+++ b/tests/write_cb_bogus_test.py
@@ -0,0 +1,44 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+import os.path
+import pycurl
+import sys
+import unittest
+
+class WriteAbortTest(unittest.TestCase):
+ def setUp(self):
+ self.curl = pycurl.Curl()
+
+ def tearDown(self):
+ self.curl.close()
+
+ def write_cb_returning_string(self, data):
+ return 'foo'
+
+ def write_cb_returning_float(self, data):
+ return 0.5
+
+ def test_write_cb_returning_string(self):
+ self.check(self.write_cb_returning_string)
+
+ def test_write_cb_returning_float(self):
+ self.check(self.write_cb_returning_float)
+
+ def check(self, write_cb):
+ # download the script itself through the file:// protocol into write_cb
+ c = pycurl.Curl()
+ self.curl.setopt(pycurl.URL, 'file://' + os.path.abspath(sys.argv[0]))
+ self.curl.setopt(pycurl.WRITEFUNCTION, write_cb)
+ try:
+ self.curl.perform()
+ except pycurl.error, (err, msg):
+ # we expect pycurl.E_WRITE_ERROR as the response
+ assert pycurl.E_WRITE_ERROR == err
+
+ # actual error
+ assert hasattr(sys, 'last_type')
+ self.assertEqual(pycurl.error, sys.last_type)
+ assert hasattr(sys, 'last_value')
+ self.assertEqual('write callback must return int or None', str(sys.last_value))
--
1.7.1
From c04e16173e67b91c47018a5f023d703f751ba23e Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Tue, 12 Mar 2013 00:00:40 -0400
Subject: [PATCH 098/149] Fix mode on write abort test
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
0 files changed, 0 insertions(+), 0 deletions(-)
mode change 100755 => 100644 tests/write_abort_test.py
diff --git a/tests/write_abort_test.py b/tests/write_abort_test.py
old mode 100755
new mode 100644
--
1.7.1
From a14d3afd57c9a402e8256a53cdcc389008356f3e Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Tue, 12 Mar 2013 00:02:56 -0400
Subject: [PATCH 099/149] This test fails intermittently, add diagnostics
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/multi_socket_select_test.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/tests/multi_socket_select_test.py b/tests/multi_socket_select_test.py
index 6db8b44..9546169 100644
--- a/tests/multi_socket_select_test.py
+++ b/tests/multi_socket_select_test.py
@@ -94,7 +94,7 @@ class MultiSocketSelectTest(unittest.TestCase):
c.http_code = c.getinfo(c.HTTP_CODE)
# at least in and remove events per socket
- assert len(socket_events) >= 6
+ assert len(socket_events) >= 6, 'Less than 6 socket events: %s' % repr(socket_events)
# print result
for c in m.handles:
--
1.7.1
From 2af85c62b266127bd9e58d1df2b1fb079a0e9654 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Tue, 12 Mar 2013 22:43:47 -0400
Subject: [PATCH 100/149] Python 2.5 compatibility: socket.create_connection
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/runwsgi.py | 14 +++++++++++++-
1 files changed, 13 insertions(+), 1 deletions(-)
diff --git a/tests/runwsgi.py b/tests/runwsgi.py
index 8a978ec..be05dfa 100644
--- a/tests/runwsgi.py
+++ b/tests/runwsgi.py
@@ -6,6 +6,18 @@ import threading
import socket
import time as _time
+try:
+ create_connection = socket.create_connection
+except AttributeError:
+ # python 2.5
+ def create_connection(netloc, timeout=None):
+ # XXX ipv4 only
+ s = socket.socket()
+ if timeout is not None:
+ s.settimeout(timeout)
+ s.connect(netloc)
+ return s
+
class Server(bottle.WSGIRefServer):
def run(self, handler): # pragma: no cover
from wsgiref.simple_server import make_server, WSGIRequestHandler
@@ -21,7 +33,7 @@ def wait_for_network_service(netloc, check_interval, num_attempts):
ok = False
for i in range(num_attempts):
try:
- conn = socket.create_connection(netloc, check_interval)
+ conn = create_connection(netloc, check_interval)
except socket.error:
e = sys.exc_info()[1]
_time.sleep(check_interval)
--
1.7.1
From d1c79723cecd7ee75181f29b46081fc49ea28790 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Tue, 12 Mar 2013 22:55:08 -0400
Subject: [PATCH 101/149] Python 2.5 compatibility: poll_interval in SocketServer
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/runwsgi.py | 17 +++++++++++++++--
1 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/tests/runwsgi.py b/tests/runwsgi.py
index be05dfa..114ce42 100644
--- a/tests/runwsgi.py
+++ b/tests/runwsgi.py
@@ -18,6 +18,8 @@ except AttributeError:
s.connect(netloc)
return s
+global_stop = False
+
class Server(bottle.WSGIRefServer):
def run(self, handler): # pragma: no cover
from wsgiref.simple_server import make_server, WSGIRequestHandler
@@ -27,7 +29,13 @@ class Server(bottle.WSGIRefServer):
def log_request(*args, **kw): pass
self.options['handler_class'] = QuietHandler
self.srv = make_server(self.host, self.port, handler, **self.options)
- self.srv.serve_forever(poll_interval=0.1)
+ if sys.version_info[0] == 2 and sys.version_info[1] < 6:
+ # python 2.5 has no poll_interval
+ # and thus no way to stop the server
+ while not global_stop:
+ self.srv.handle_request()
+ else:
+ self.srv.serve_forever(poll_interval=0.1)
def wait_for_network_service(netloc, check_interval, num_attempts):
ok = False
@@ -104,6 +112,11 @@ def app_runner_setup(*specs):
for server in self.servers:
# if no tests from module were run, there is no server to shut down
if hasattr(server, 'srv'):
- server.srv.shutdown()
+ if hasattr(server.srv, 'shutdown'):
+ server.srv.shutdown()
+ else:
+ # python 2.5
+ global global_stop
+ global_stop = True
return [setup, teardown]
--
1.7.1
From cb24d7050133cbed97c7f05ef6f649c7a7bfede3 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Fri, 15 Mar 2013 12:28:11 -0400
Subject: [PATCH 102/149] Tests do not call global cleanup for now
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/reset_test.py | 2 --
1 files changed, 0 insertions(+), 2 deletions(-)
diff --git a/tests/reset_test.py b/tests/reset_test.py
index cc55f86..61a692a 100644
--- a/tests/reset_test.py
+++ b/tests/reset_test.py
@@ -72,5 +72,3 @@ class ResetTest(unittest.TestCase):
eh.close()
cm.close()
outf.close()
-
- pycurl.global_cleanup()
--
1.7.1
From 7a74c1e39377d4220ffafe335a71487515bede8e Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Fri, 15 Mar 2013 17:01:35 -0400
Subject: [PATCH 103/149] Python 3 compatibility: except syntax
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/write_abort_test.py | 3 ++-
tests/write_cb_bogus_test.py | 3 ++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/tests/write_abort_test.py b/tests/write_abort_test.py
index 3272961..957fe78 100644
--- a/tests/write_abort_test.py
+++ b/tests/write_abort_test.py
@@ -24,7 +24,8 @@ class WriteAbortTest(unittest.TestCase):
self.curl.setopt(pycurl.WRITEFUNCTION, write_cb)
try:
self.curl.perform()
- except pycurl.error, (err, msg):
+ except pycurl.error:
+ err, msg = sys.exc_info()[1]
# we expect pycurl.E_WRITE_ERROR as the response
assert pycurl.E_WRITE_ERROR == err
diff --git a/tests/write_cb_bogus_test.py b/tests/write_cb_bogus_test.py
index 4bec2ad..ef709db 100644
--- a/tests/write_cb_bogus_test.py
+++ b/tests/write_cb_bogus_test.py
@@ -33,7 +33,8 @@ class WriteAbortTest(unittest.TestCase):
self.curl.setopt(pycurl.WRITEFUNCTION, write_cb)
try:
self.curl.perform()
- except pycurl.error, (err, msg):
+ except pycurl.error:
+ err, msg = sys.exc_info()[1]
# we expect pycurl.E_WRITE_ERROR as the response
assert pycurl.E_WRITE_ERROR == err
--
1.7.1
From 84bb12176059f28c0cc030d6b620656a00fff3a4 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Fri, 15 Mar 2013 21:21:50 -0400
Subject: [PATCH 104/149] Python 3 compatibility: map
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/util.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/tests/util.py b/tests/util.py
index b8e22ec..1e9f406 100644
--- a/tests/util.py
+++ b/tests/util.py
@@ -24,7 +24,7 @@ def version_less_than_spec(version_tuple, spec_tuple):
return False
def pycurl_version_less_than(*spec):
- version = map(int, pycurl.version_info()[1].split('.'))
+ version = [int(part) for part in pycurl.version_info()[1].split('.')]
return version_less_than_spec(version, spec)
#
--
1.7.1
From d30ec2cf96c66cd176bb999fb7a3a39d13ddf0dd Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Fri, 15 Mar 2013 21:27:58 -0400
Subject: [PATCH 105/149] Python 3 compatibility: apply no longer exists
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
python/curl/__init__.py | 4 ++--
setup.py | 3 +--
setup_win32_ssl.py | 2 +-
3 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/python/curl/__init__.py b/python/curl/__init__.py
index b002618..5b6b549 100644
--- a/python/curl/__init__.py
+++ b/python/curl/__init__.py
@@ -63,7 +63,7 @@ class Curl:
def set_option(self, *args):
"Set an option on the retrieval."
- apply(self.handle.setopt, args)
+ self.handle.setopt(*args)
def set_verbosity(self, level):
"Set verbosity to 1 to see transactions."
@@ -103,7 +103,7 @@ class Curl:
def get_info(self, *args):
"Get information about retrieval."
- return apply(self.handle.getinfo, args)
+ return self.handle.getinfo(*args)
def info(self):
"Return a dictionary with all info on the last response."
diff --git a/setup.py b/setup.py
index 235e4c9..013bd11 100644
--- a/setup.py
+++ b/setup.py
@@ -221,5 +221,4 @@ if LooseVersion(distutils.__version__) < LooseVersion("1.0.3"):
if __name__ == "__main__":
for o in ext.extra_objects:
assert os.path.isfile(o), o
- # We can live with the deprecationwarning for a while
- apply(setup, (), setup_args)
+ setup(**setup_args)
diff --git a/setup_win32_ssl.py b/setup_win32_ssl.py
index 332c04c..0ecc399 100644
--- a/setup_win32_ssl.py
+++ b/setup_win32_ssl.py
@@ -32,5 +32,5 @@ ext.extra_objects.append(r"c:\src\pool\libidn-0.5.15" + pool + "idn.lib")
if __name__ == "__main__":
for o in ext.extra_objects:
assert os.path.isfile(o), o
- apply(setup, (), setup_args)
+ setup(**setup_args)
--
1.7.1
From c903dc9eebdf010bfc8142b5f47cd564e04baa19 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Tue, 26 Feb 2013 16:40:28 -0500
Subject: [PATCH 106/149] Python 3 compatibility: exception raising syntax
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
setup.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/setup.py b/setup.py
index 013bd11..725007c 100644
--- a/setup.py
+++ b/setup.py
@@ -108,7 +108,7 @@ else:
if p.wait() == 0:
optbuf += stdout
if optbuf == "":
- raise Exception, ("Neither of curl-config --libs or --static-libs" +
+ raise Exception("Neither of curl-config --libs or --static-libs" +
"produced output")
libs = split_quoted(optbuf)
--
1.7.1
From 2643ab0cd015f09b1a0fd928c8e1102eee8e203e Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 27 Feb 2013 20:23:28 -0500
Subject: [PATCH 107/149] Python 3 compatibility: print syntax (src)
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
python/curl/__init__.py | 8 ++++----
1 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/python/curl/__init__.py b/python/curl/__init__.py
index 5b6b549..5617262 100644
--- a/python/curl/__init__.py
+++ b/python/curl/__init__.py
@@ -164,10 +164,10 @@ if __name__ == "__main__":
url = sys.argv[1]
c = Curl()
c.get(url)
- print c.body()
- print '='*74 + '\n'
+ print(c.body())
+ print('='*74 + '\n')
import pprint
pprint.pprint(c.info())
- print c.get_info(pycurl.OS_ERRNO)
- print c.info()['os-errno']
+ print(c.get_info(pycurl.OS_ERRNO))
+ print(c.info()['os-errno'])
c.close()
--
1.7.1
From c2f5306b77da24f1cb1cf45b9dbda93b817bf6e6 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Tue, 26 Feb 2013 16:49:09 -0500
Subject: [PATCH 108/149] Python 3 compatibility: print syntax (former tests)
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
examples/tests/test_gtk.py | 2 +-
examples/tests/test_xmlrpc.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/examples/tests/test_gtk.py b/examples/tests/test_gtk.py
index 7104439..da8c22a 100644
--- a/examples/tests/test_gtk.py
+++ b/examples/tests/test_gtk.py
@@ -83,7 +83,7 @@ class Test(threading.Thread):
# Check command line args
if len(sys.argv) < 3:
- print "Usage: %s <URL> <filename>" % sys.argv[0]
+ print("Usage: %s <URL> <filename>" % sys.argv[0])
raise SystemExit
# Make a progress bar window
diff --git a/examples/tests/test_xmlrpc.py b/examples/tests/test_xmlrpc.py
index bc5953e..3a5469a 100644
--- a/examples/tests/test_xmlrpc.py
+++ b/examples/tests/test_xmlrpc.py
@@ -24,6 +24,6 @@ c.setopt(c.POST, 1)
c.setopt(c.HTTPHEADER, xmlrpc_header)
c.setopt(c.POSTFIELDS, xmlrpc_template % ("examples.getStateName", xmlrpclib.dumps((5,))))
-print 'Response from http://betty.userland.com/'
+print('Response from http://betty.userland.com/')
c.perform()
c.close()
--
1.7.1
From 0106dfe86ca2a5620f07c2687cb05502636298ce Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 27 Feb 2013 20:23:39 -0500
Subject: [PATCH 109/149] Python 3 compatibility: print syntax (examples)
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
examples/basicfirst.py | 4 +-
examples/file_upload.py | 6 +-
examples/linksys.py | 182 +++++++++++++++++++++---------------------
examples/retriever-multi.py | 10 +-
examples/retriever.py | 6 +-
examples/sfquery.py | 2 +-
examples/xmlrpc_curl.py | 6 +-
7 files changed, 108 insertions(+), 108 deletions(-)
diff --git a/examples/basicfirst.py b/examples/basicfirst.py
index af59405..44060af 100644
--- a/examples/basicfirst.py
+++ b/examples/basicfirst.py
@@ -13,7 +13,7 @@ class Test:
def body_callback(self, buf):
self.contents = self.contents + buf
-print >>sys.stderr, 'Testing', pycurl.version
+sys.stderr.write("Testing %s\n" % pycurl.version)
t = Test()
c = pycurl.Curl()
@@ -22,4 +22,4 @@ c.setopt(c.WRITEFUNCTION, t.body_callback)
c.perform()
c.close()
-print t.contents
+print(t.contents)
diff --git a/examples/file_upload.py b/examples/file_upload.py
index a514c1c..7750865 100644
--- a/examples/file_upload.py
+++ b/examples/file_upload.py
@@ -15,13 +15,13 @@ class FileReader:
# Check commandline arguments
if len(sys.argv) < 3:
- print "Usage: %s <url> <file to upload>" % sys.argv[0]
+ print("Usage: %s <url> <file to upload>" % sys.argv[0])
raise SystemExit
url = sys.argv[1]
filename = sys.argv[2]
if not os.path.exists(filename):
- print "Error: the file '%s' does not exist" % filename
+ print("Error: the file '%s' does not exist" % filename)
raise SystemExit
# Initialize pycurl
@@ -41,6 +41,6 @@ filesize = os.path.getsize(filename)
c.setopt(pycurl.INFILESIZE, filesize)
# Start transfer
-print 'Uploading file %s to url %s' % (filename, url)
+print('Uploading file %s to url %s' % (filename, url))
c.perform()
c.close()
diff --git a/examples/linksys.py b/examples/linksys.py
index 5304886..24cb2a9 100755
--- a/examples/linksys.py
+++ b/examples/linksys.py
@@ -224,11 +224,11 @@ if __name__ == "__main__":
self.session = LinksysSession()
if os.isatty(0):
import readline
- print "Type ? or `help' for help."
+ print("Type ? or `help' for help.")
self.prompt = self.session.host + ": "
else:
self.prompt = ""
- print "Bar1"
+ print("Bar1")
def flag_command(self, func, line):
if line.strip() in ("on", "enable", "yes"):
@@ -246,96 +246,96 @@ if __name__ == "__main__":
self.session.cache_flush()
self.prompt = self.session.host + ": "
else:
- print self.session.host
+ print(self.session.host)
return 0
def help_connect(self):
- print "Usage: connect [<hostname-or-IP>]"
- print "Connect to a Linksys by name or IP address."
- print "If no argument is given, print the current host."
+ print("Usage: connect [<hostname-or-IP>]")
+ print("Connect to a Linksys by name or IP address.")
+ print("If no argument is given, print the current host.")
def do_status(self, line):
self.session.cache_load("")
if "" in self.session.pagecache:
- print "Firmware:", self.session.get_firmware_version()
- print "LAN MAC:", self.session.get_LAN_MAC()
- print "Wireless MAC:", self.session.get_Wireless_MAC()
- print "WAN MAC:", self.session.get_WAN_MAC()
- print "."
+ print("Firmware:", self.session.get_firmware_version())
+ print("LAN MAC:", self.session.get_LAN_MAC())
+ print("Wireless MAC:", self.session.get_Wireless_MAC())
+ print("WAN MAC:", self.session.get_WAN_MAC())
+ print(".")
return 0
def help_status(self):
- print "Usage: status"
- print "The status command shows the status of the Linksys."
- print "It is mainly useful as a sanity check to make sure"
- print "the box is responding correctly."
+ print("Usage: status")
+ print("The status command shows the status of the Linksys.")
+ print("It is mainly useful as a sanity check to make sure")
+ print("the box is responding correctly.")
def do_verbose(self, line):
self.flag_command(self.session.set_verbosity, line)
def help_verbose(self):
- print "Usage: verbose {on|off|enable|disable|yes|no}"
- print "Enables display of HTTP requests."
+ print("Usage: verbose {on|off|enable|disable|yes|no}")
+ print("Enables display of HTTP requests.")
def do_host(self, line):
self.session.set_host_name(line)
return 0
def help_host(self):
- print "Usage: host <hostname>"
- print "Sets the Host field to be queried by the ISP."
+ print("Usage: host <hostname>")
+ print("Sets the Host field to be queried by the ISP.")
def do_domain(self, line):
- print "Usage: host <domainname>"
+ print("Usage: host <domainname>")
self.session.set_domain_name(line)
return 0
def help_domain(self):
- print "Sets the Domain field to be queried by the ISP."
+ print("Sets the Domain field to be queried by the ISP.")
def do_lan_address(self, line):
self.session.set_LAN_IP(line)
return 0
def help_lan_address(self):
- print "Usage: lan_address <ip-address>"
- print "Sets the LAN IP address."
+ print("Usage: lan_address <ip-address>")
+ print("Sets the LAN IP address.")
def do_lan_netmask(self, line):
self.session.set_LAN_netmask(line)
return 0
def help_lan_netmask(self):
- print "Usage: lan_netmask <ip-mask>"
- print "Sets the LAN subnetwork mask."
+ print("Usage: lan_netmask <ip-mask>")
+ print("Sets the LAN subnetwork mask.")
def do_wireless(self, line):
self.flag_command(self.session.set_wireless, line)
return 0
def help_wireless(self):
- print "Usage: wireless {on|off|enable|disable|yes|no}"
- print "Switch to enable or disable wireless features."
+ print("Usage: wireless {on|off|enable|disable|yes|no}")
+ print("Switch to enable or disable wireless features.")
def do_ssid(self, line):
self.session.set_SSID(line)
return 0
def help_ssid(self):
- print "Usage: ssid <string>"
- print "Sets the SSID used to control wireless access."
+ print("Usage: ssid <string>")
+ print("Sets the SSID used to control wireless access.")
def do_ssid_broadcast(self, line):
self.flag_command(self.session.set_SSID_broadcast, line)
return 0
def help_ssid_broadcast(self):
- print "Usage: ssid_broadcast {on|off|enable|disable|yes|no}"
- print "Switch to enable or disable SSID broadcast."
+ print("Usage: ssid_broadcast {on|off|enable|disable|yes|no}")
+ print("Switch to enable or disable SSID broadcast.")
def do_channel(self, line):
self.session.set_channel(line)
return 0
def help_channel(self):
- print "Usage: channel <number>"
- print "Sets the wireless channel."
+ print("Usage: channel <number>")
+ print("Sets the wireless channel.")
def do_wep(self, line):
self.flag_command(self.session.set_WEP, line)
return 0
def help_wep(self):
- print "Usage: wep {on|off|enable|disable|yes|no}"
- print "Switch to enable or disable WEP security."
+ print("Usage: wep {on|off|enable|disable|yes|no}")
+ print("Switch to enable or disable WEP security.")
def do_wan_type(self, line):
try:
@@ -345,29 +345,29 @@ if __name__ == "__main__":
print >>sys.stderr, "linksys: unknown connection type."
return 0
def help_wan_type(self):
- print "Usage: wan_type {auto|static|ppoe|ras|pptp|heartbeat}"
- print "Set the WAN connection type."
+ print("Usage: wan_type {auto|static|ppoe|ras|pptp|heartbeat}")
+ print("Set the WAN connection type.")
def do_wan_address(self, line):
self.session.set_WAN_IP(line)
return 0
def help_wan_address(self):
- print "Usage: wan_address <ip-address>"
- print "Sets the WAN IP address."
+ print("Usage: wan_address <ip-address>")
+ print("Sets the WAN IP address.")
def do_wan_netmask(self, line):
self.session.set_WAN_netmask(line)
return 0
def help_wan_netmask(self):
- print "Usage: wan_netmask <ip-mask>"
- print "Sets the WAN subnetwork mask."
+ print("Usage: wan_netmask <ip-mask>")
+ print("Sets the WAN subnetwork mask.")
def do_wan_gateway(self, line):
self.session.set_WAN_gateway(line)
return 0
def help_wan_gateway(self):
- print "Usage: wan_gateway <ip-address>"
- print "Sets the LAN subnetwork mask."
+ print("Usage: wan_gateway <ip-address>")
+ print("Sets the LAN subnetwork mask.")
def do_dns(self, line):
(index, address) = line.split()
@@ -377,52 +377,52 @@ if __name__ == "__main__":
print >>sys.stderr, "linksys: server index out of bounds."
return 0
def help_dns(self):
- print "Usage: dns {1|2|3} <ip-mask>"
- print "Sets a primary, secondary, or tertiary DNS server address."
+ print("Usage: dns {1|2|3} <ip-mask>")
+ print("Sets a primary, secondary, or tertiary DNS server address.")
def do_password(self, line):
self.session.set_password(line)
return 0
def help_password(self):
- print "Usage: password <string>"
- print "Sets the router password."
+ print("Usage: password <string>")
+ print("Sets the router password.")
def do_upnp(self, line):
self.flag_command(self.session.set_UPnP, line)
return 0
def help_upnp(self):
- print "Usage: upnp {on|off|enable|disable|yes|no}"
- print "Switch to enable or disable Universal Plug and Play."
+ print("Usage: upnp {on|off|enable|disable|yes|no}")
+ print("Switch to enable or disable Universal Plug and Play.")
def do_reset(self, line):
self.session.reset()
def help_reset(self):
- print "Usage: reset"
- print "Reset Linksys settings to factory defaults."
+ print("Usage: reset")
+ print("Reset Linksys settings to factory defaults.")
def do_dhcp(self, line):
self.flag_command(self.session.set_DHCP, line)
def help_dhcp(self):
- print "Usage: dhcp {on|off|enable|disable|yes|no}"
- print "Switch to enable or disable DHCP features."
+ print("Usage: dhcp {on|off|enable|disable|yes|no}")
+ print("Switch to enable or disable DHCP features.")
def do_dhcp_start(self, line):
self.session.set_DHCP_starting_IP(line)
def help_dhcp_start(self):
- print "Usage: dhcp_start <number>"
- print "Set the start address of the DHCP pool."
+ print("Usage: dhcp_start <number>")
+ print("Set the start address of the DHCP pool.")
def do_dhcp_users(self, line):
self.session.set_DHCP_users(line)
def help_dhcp_users(self):
- print "Usage: dhcp_users <number>"
- print "Set number of address slots to allocate in the DHCP pool."
+ print("Usage: dhcp_users <number>")
+ print("Set number of address slots to allocate in the DHCP pool.")
def do_dhcp_lease(self, line):
self.session.set_DHCP_lease(line)
def help_dhcp_lease(self):
- print "Usage: dhcp_lease <number>"
- print "Set number of address slots to allocate in the DHCP pool."
+ print("Usage: dhcp_lease <number>")
+ print("Set number of address slots to allocate in the DHCP pool.")
def do_dhcp_dns(self, line):
(index, address) = line.split()
@@ -432,46 +432,46 @@ if __name__ == "__main__":
print >>sys.stderr, "linksys: server index out of bounds."
return 0
def help_dhcp_dns(self):
- print "Usage: dhcp_dns {1|2|3} <ip-mask>"
- print "Sets primary, secondary, or tertiary DNS server address."
+ print("Usage: dhcp_dns {1|2|3} <ip-mask>")
+ print("Sets primary, secondary, or tertiary DNS server address.")
def do_logging(self, line):
self.flag_command(self.session.set_logging, line)
def help_logging(self):
- print "Usage: logging {on|off|enable|disable|yes|no}"
- print "Switch to enable or disable session logging."
+ print("Usage: logging {on|off|enable|disable|yes|no}")
+ print("Switch to enable or disable session logging.")
def do_log_address(self, line):
self.session.set_Log_address(line)
def help_log_address(self):
- print "Usage: log_address <number>"
- print "Set the last quad of the address to which to log."
+ print("Usage: log_address <number>")
+ print("Set the last quad of the address to which to log.")
def do_configure(self, line):
self.session.configure()
return 0
def help_configure(self):
- print "Usage: configure"
- print "Writes the configuration to the Linksys."
+ print("Usage: configure")
+ print("Writes the configuration to the Linksys.")
def do_cache(self, line):
- print self.session.pagecache
+ print(self.session.pagecache)
def help_cache(self):
- print "Usage: cache"
- print "Display the page cache."
+ print("Usage: cache")
+ print("Display the page cache.")
def do_quit(self, line):
return 1
def help_quit(self, line):
- print "The quit command ends your linksys session without"
- print "writing configuration changes to the Linksys."
+ print("The quit command ends your linksys session without")
+ print("writing configuration changes to the Linksys.")
def do_EOF(self, line):
- print ""
+ print("")
self.session.configure()
return 1
def help_EOF(self):
- print "The EOF command writes the configuration to the linksys"
- print "and ends your session."
+ print("The EOF command writes the configuration to the linksys")
+ print("and ends your session.")
def default(self, line):
"""Pass the command through to be executed by the shell."""
@@ -479,11 +479,11 @@ if __name__ == "__main__":
return 0
def help_help(self):
- print "On-line help is available through this command."
- print "? is a convenience alias for help."
+ print("On-line help is available through this command.")
+ print("? is a convenience alias for help.")
def help_introduction(self):
- print """\
+ print("""\
This program supports changing the settings on Linksys blue-box routers. This
capability may come in handy when they freeze up and have to be reset. Though
@@ -506,16 +506,16 @@ will be shipped to the Linksys at the end of session (e.g. when the program
running in batch mode encounters end-of-file or you type a control-D). If you
end the session with `quit', pending changes will be discarded.
-For more help, read the topics 'wan', 'lan', and 'wireless'."""
+For more help, read the topics 'wan', 'lan', and 'wireless'.""")
def help_lan(self):
- print """\
+ print("""\
The `lan_address' and `lan_netmask' commands let you set the IP location of
the Linksys on your LAN, or inside. Normally you'll want to leave these
-untouched."""
+untouched.""")
def help_wan(self):
- print """\
+ print("""\
The WAN commands become significant if you are using the BEFSR41 or any of
the other Linksys boxes designed as DSL or cable-modem gateways. You will
need to use `wan_type' to declare how you expect to get your address.
@@ -527,22 +527,22 @@ need to use the `dns' command to declare which remote servers your DNS
requests should be forwarded to.
Some ISPs may require you to set host and domain for use with dynamic-address
-allocation."""
+allocation.""")
def help_wireless(self):
- print """\
+ print("""\
The channel, ssid, ssid_broadcast, wep, and wireless commands control
-wireless routing."""
+wireless routing.""")
def help_switches(self):
- print "Switches may be turned on with 'on', 'enable', or 'yes'."
- print "Switches may be turned off with 'off', 'disable', or 'no'."
- print "Switch commands include: wireless, ssid_broadcast."
+ print("Switches may be turned on with 'on', 'enable', or 'yes'.")
+ print("Switches may be turned off with 'off', 'disable', or 'no'.")
+ print("Switch commands include: wireless, ssid_broadcast.")
def help_addresses(self):
- print "An address argument must be a valid IP address;"
- print "four decimal numbers separated by dots, each "
- print "between 0 and 255."
+ print("An address argument must be a valid IP address;")
+ print("four decimal numbers separated by dots, each ")
+ print("between 0 and 255.")
def emptyline(self):
pass
diff --git a/examples/retriever-multi.py b/examples/retriever-multi.py
index 1a941be..ad4cebd 100644
--- a/examples/retriever-multi.py
+++ b/examples/retriever-multi.py
@@ -31,7 +31,7 @@ try:
if len(sys.argv) >= 3:
num_conn = int(sys.argv[2])
except:
- print "Usage: %s <file with URLs to fetch> [<# of concurrent connections>]" % sys.argv[0]
+ print("Usage: %s <file with URLs to fetch> [<# of concurrent connections>]" % sys.argv[0])
raise SystemExit
@@ -50,8 +50,8 @@ assert queue, "no URLs given"
num_urls = len(queue)
num_conn = min(num_conn, num_urls)
assert 1 <= num_conn <= 10000, "invalid number of concurrent connections"
-print "PycURL %s (compiled against 0x%x)" % (pycurl.version, pycurl.COMPILE_LIBCURL_VERSION_NUM)
-print "----- Getting", num_urls, "URLs using", num_conn, "connections -----"
+print("PycURL %s (compiled against 0x%x)" % (pycurl.version, pycurl.COMPILE_LIBCURL_VERSION_NUM))
+print("----- Getting", num_urls, "URLs using", num_conn, "connections -----")
# Pre-allocate a list of curl objects
@@ -95,13 +95,13 @@ while num_processed < num_urls:
c.fp.close()
c.fp = None
m.remove_handle(c)
- print "Success:", c.filename, c.url, c.getinfo(pycurl.EFFECTIVE_URL)
+ print("Success:", c.filename, c.url, c.getinfo(pycurl.EFFECTIVE_URL))
freelist.append(c)
for c, errno, errmsg in err_list:
c.fp.close()
c.fp = None
m.remove_handle(c)
- print "Failed: ", c.filename, c.url, errno, errmsg
+ print("Failed: ", c.filename, c.url, errno, errmsg)
freelist.append(c)
num_processed = num_processed + len(ok_list) + len(err_list)
if num_q == 0:
diff --git a/examples/retriever.py b/examples/retriever.py
index 2c91d07..be1b6ea 100644
--- a/examples/retriever.py
+++ b/examples/retriever.py
@@ -31,7 +31,7 @@ try:
if len(sys.argv) >= 3:
num_conn = int(sys.argv[2])
except:
- print "Usage: %s <file with URLs to fetch> [<# of concurrent connections>]" % sys.argv[0]
+ print("Usage: %s <file with URLs to fetch> [<# of concurrent connections>]" % sys.argv[0])
raise SystemExit
@@ -50,8 +50,8 @@ assert queue.queue, "no URLs given"
num_urls = len(queue.queue)
num_conn = min(num_conn, num_urls)
assert 1 <= num_conn <= 10000, "invalid number of concurrent connections"
-print "PycURL %s (compiled against 0x%x)" % (pycurl.version, pycurl.COMPILE_LIBCURL_VERSION_NUM)
-print "----- Getting", num_urls, "URLs using", num_conn, "connections -----"
+print("PycURL %s (compiled against 0x%x)" % (pycurl.version, pycurl.COMPILE_LIBCURL_VERSION_NUM))
+print("----- Getting", num_urls, "URLs using", num_conn, "connections -----")
class WorkerThread(threading.Thread):
diff --git a/examples/sfquery.py b/examples/sfquery.py
index 741feb0..16aa9d4 100644
--- a/examples/sfquery.py
+++ b/examples/sfquery.py
@@ -40,7 +40,7 @@ if __name__ == "__main__":
name, account, password = auth
except:
if len(sys.argv) < 4:
- print "Usage: %s <project id> <username> <password>" % sys.argv[0]
+ print("Usage: %s <project id> <username> <password>" % sys.argv[0])
raise SystemExit
name = sys.argv[2]
password = sys.argv[3]
diff --git a/examples/xmlrpc_curl.py b/examples/xmlrpc_curl.py
index ec0df50..a9a7d9e 100644
--- a/examples/xmlrpc_curl.py
+++ b/examples/xmlrpc_curl.py
@@ -55,8 +55,8 @@ if __name__ == "__main__":
## Test
server = xmlrpclib.ServerProxy("http://betty.userland.com",
transport=CURLTransport())
- print server
+ print(server)
try:
- print server.examples.getStateName(41)
+ print(server.examples.getStateName(41))
except xmlrpclib.Error, v:
- print "ERROR", v
+ print("ERROR", v)
--
1.7.1
From bb811ad5b2d2819d502c84c735b7efec84d4d557 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Thu, 28 Feb 2013 00:30:00 -0500
Subject: [PATCH 110/149] Python 3 compatibility: printing to stderr (linksys example)
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
examples/linksys.py | 14 +++++++++-----
1 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/examples/linksys.py b/examples/linksys.py
index 24cb2a9..f66ff0d 100755
--- a/examples/linksys.py
+++ b/examples/linksys.py
@@ -34,6 +34,10 @@
import sys, re, copy, curl, exceptions
+def print_stderr(arg):
+ sys.stderr.write(arg)
+ sys.stderr.write("\n")
+
class LinksysError(exceptions.Exception):
def __init__(self, *args):
self.args = args
@@ -202,7 +206,7 @@ class LinksysSession:
for (page, field, value) in self.actions:
self.cache_load(page)
if self.pagecache[page].find(field) == -1:
- print >>sys.stderr, "linksys: field %s not found where expected in page %s!" % (field, os.path.join(self.host, page))
+ print_stderr("linksys: field %s not found where expected in page %s!" % (field, os.path.join(self.host, page)))
continue
else:
fields.append((field, value))
@@ -236,7 +240,7 @@ if __name__ == "__main__":
elif line.strip() in ("off", "disable", "no"):
func(False)
else:
- print >>sys.stderr, "linksys: unknown switch value"
+ print_stderr("linksys: unknown switch value")
return 0
def do_connect(self, line):
@@ -342,7 +346,7 @@ if __name__ == "__main__":
type=eval("LinksysSession.WAN_CONNECT_"+line.strip().upper())
self.session.set_connection_type(type)
except ValueError:
- print >>sys.stderr, "linksys: unknown connection type."
+ print_stderr("linksys: unknown connection type.")
return 0
def help_wan_type(self):
print("Usage: wan_type {auto|static|ppoe|ras|pptp|heartbeat}")
@@ -374,7 +378,7 @@ if __name__ == "__main__":
if index in ("1", "2", "3"):
self.session.set_DNS_server(eval(index), address)
else:
- print >>sys.stderr, "linksys: server index out of bounds."
+ print_stderr("linksys: server index out of bounds.")
return 0
def help_dns(self):
print("Usage: dns {1|2|3} <ip-mask>")
@@ -429,7 +433,7 @@ if __name__ == "__main__":
if index in ("1", "2", "3"):
self.session.set_DHCP_DNS_server(eval(index), address)
else:
- print >>sys.stderr, "linksys: server index out of bounds."
+ print_stderr("linksys: server index out of bounds.")
return 0
def help_dhcp_dns(self):
print("Usage: dhcp_dns {1|2|3} <ip-mask>")
--
1.7.1
From 2ea28565bcf16bb2e792467650012081e2268cf1 Mon Sep 17 00:00:00 2001
From: Kamil Dudka <kdudka@redhat.com>
Date: Mon, 8 Apr 2013 14:25:26 +0200
Subject: [PATCH 111/149] allow to unset a previously set RANGE value
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
... and add the unset_range_test.py unit test exercising it
Reported by: Zdeněk Pavlas
Bug: https://bugzilla.redhat.com/928370
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
src/pycurl.c | 1 +
tests/unset_range_test.py | 39 +++++++++++++++++++++++++++++++++++++++
2 files changed, 40 insertions(+), 0 deletions(-)
create mode 100644 tests/unset_range_test.py
diff --git a/src/pycurl.c b/src/pycurl.c
index a30c339..bd28b81 100644
--- a/src/pycurl.c
+++ b/src/pycurl.c
@@ -1561,6 +1561,7 @@ util_curl_unsetopt(CurlObject *self, int option)
case CURLOPT_RANDOM_FILE:
case CURLOPT_SSL_CIPHER_LIST:
case CURLOPT_USERPWD:
+ case CURLOPT_RANGE:
SETOPT((char *) 0);
break;
diff --git a/tests/unset_range_test.py b/tests/unset_range_test.py
new file mode 100644
index 0000000..4400851
--- /dev/null
+++ b/tests/unset_range_test.py
@@ -0,0 +1,39 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+import os.path
+import pycurl
+import sys
+import unittest
+
+class UnsetRangeTest(unittest.TestCase):
+ def setUp(self):
+ self.curl = pycurl.Curl()
+
+ def tearDown(self):
+ self.curl.close()
+
+ def test_unset_range(self):
+ def write_cb(data):
+ self.read += len(data)
+ return None
+
+ # download bytes 0-9 of the script itself through the file:// protocol
+ self.read = 0
+ self.curl.setopt(pycurl.URL, 'file://' + os.path.abspath(sys.argv[0]))
+ self.curl.setopt(pycurl.WRITEFUNCTION, write_cb)
+ self.curl.setopt(pycurl.RANGE, '0-9')
+ self.curl.perform()
+ assert 10 == self.read
+
+ # the RANGE setting should be preserved from the previous transfer
+ self.read = 0
+ self.curl.perform()
+ assert 10 == self.read
+
+ # drop the RANGE setting using unsetopt() and download entire script
+ self.read = 0
+ self.curl.unsetopt(pycurl.RANGE)
+ self.curl.perform()
+ assert 10 < self.read
--
1.7.1
From 576e87580356497b601ec16a9a794db959d43f0e Mon Sep 17 00:00:00 2001
From: Kamil Dudka <kdudka@redhat.com>
Date: Mon, 8 Apr 2013 14:31:30 +0200
Subject: [PATCH 112/149] allow to use setopt(..., None) as unsetopt()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
... and extend the unset_range_test.py unit test to exercise it
Reported by: Zdeněk Pavlas
Bug: https://bugzilla.redhat.com/928370
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
src/pycurl.c | 4 +---
tests/unset_range_test.py | 12 ++++++++++++
2 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/src/pycurl.c b/src/pycurl.c
index bd28b81..619ca20 100644
--- a/src/pycurl.c
+++ b/src/pycurl.c
@@ -1631,12 +1631,10 @@ do_curl_setopt(CurlObject *self, PyObject *args)
if (option % 10000 >= OPTIONS_SIZE)
goto error;
-#if 0 /* XXX - should we ??? */
- /* Handle the case of None */
+ /* Handle the case of None as the call of unsetopt() */
if (obj == Py_None) {
return util_curl_unsetopt(self, option);
}
-#endif
/* Handle the case of string arguments */
if (PyString_Check(obj)) {
diff --git a/tests/unset_range_test.py b/tests/unset_range_test.py
index 4400851..10ee801 100644
--- a/tests/unset_range_test.py
+++ b/tests/unset_range_test.py
@@ -37,3 +37,15 @@ class UnsetRangeTest(unittest.TestCase):
self.curl.unsetopt(pycurl.RANGE)
self.curl.perform()
assert 10 < self.read
+
+ # now set the RANGE again and check that pycurl takes it into account
+ self.read = 0
+ self.curl.setopt(pycurl.RANGE, '0-9')
+ self.curl.perform()
+ assert 10 == self.read
+
+ # now drop the RANGE setting using setopt(..., None)
+ self.read = 0
+ self.curl.setopt(pycurl.RANGE, None)
+ self.curl.perform()
+ assert 10 < self.read
--
1.7.1
From 5c27a721fc3aaeaeddabb394ae6c9502391bdea6 Mon Sep 17 00:00:00 2001
From: Zdenek Pavlas <zpavlas@redhat.com>
Date: Wed, 13 Mar 2013 16:55:58 +0100
Subject: [PATCH 113/149] add the GLOBAL_ACK_EINTR constant to the list of exported symbols
... if built against a new enough version of libcurl
Bug: https://bugzilla.redhat.com/920589
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
src/pycurl.c | 19 +++++++++++++++----
1 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/src/pycurl.c b/src/pycurl.c
index 619ca20..9950e00 100644
--- a/src/pycurl.c
+++ b/src/pycurl.c
@@ -3210,6 +3210,16 @@ static PyTypeObject CurlMulti_Type = {
*/
};
+static int
+are_global_init_flags_valid(int flags)
+{
+#ifdef CURL_GLOBAL_ACK_EINTR
+ /* CURL_GLOBAL_ACK_EINTR was introduced in libcurl-7.30.0 */
+ return !(flags & ~(CURL_GLOBAL_ALL | CURL_GLOBAL_ACK_EINTR));
+#else
+ return !(flags & ~(CURL_GLOBAL_ALL));
+#endif
+}
/*************************************************************************
// module level
@@ -3227,10 +3237,7 @@ do_global_init(PyObject *dummy, PyObject *args)
return NULL;
}
- if (!(option == CURL_GLOBAL_SSL ||
- option == CURL_GLOBAL_WIN32 ||
- option == CURL_GLOBAL_ALL ||
- option == CURL_GLOBAL_NOTHING)) {
+ if (!are_global_init_flags_valid(option)) {
PyErr_SetString(PyExc_ValueError, "invalid option to global_init");
return NULL;
}
@@ -3866,6 +3873,10 @@ initpycurl(void)
insint(d, "GLOBAL_ALL", CURL_GLOBAL_ALL);
insint(d, "GLOBAL_NOTHING", CURL_GLOBAL_NOTHING);
insint(d, "GLOBAL_DEFAULT", CURL_GLOBAL_DEFAULT);
+#ifdef CURL_GLOBAL_ACK_EINTR
+ /* CURL_GLOBAL_ACK_EINTR was introduced in libcurl-7.30.0 */
+ insint(d, "GLOBAL_ACK_EINTR", CURL_GLOBAL_ACK_EINTR);
+#endif
/* constants for curl_multi_socket interface */
--
1.7.1
From 5a165baaa2b275293c1e045d8c0023d55bd0cbfc Mon Sep 17 00:00:00 2001
From: Kamil Dudka <kdudka@redhat.com>
Date: Wed, 3 Apr 2013 15:06:32 +0200
Subject: [PATCH 114/149] tests/global_init_ack_eintr.py: test GLOBAL_ACK_EINTR
... if we have a new enough version of libcurl
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/global_init_ack_eintr.py | 22 ++++++++++++++++++++++
1 files changed, 22 insertions(+), 0 deletions(-)
create mode 100644 tests/global_init_ack_eintr.py
diff --git a/tests/global_init_ack_eintr.py b/tests/global_init_ack_eintr.py
new file mode 100644
index 0000000..429fc3f
--- /dev/null
+++ b/tests/global_init_ack_eintr.py
@@ -0,0 +1,22 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+import pycurl
+import unittest
+
+from . import util
+
+class GlobalInitAckEintrTest(unittest.TestCase):
+ def test_global_init_default(self):
+ # initialize libcurl with DEFAULT flags
+ pycurl.global_init(pycurl.GLOBAL_DEFAULT)
+ pycurl.global_cleanup()
+
+ def test_global_init_ack_eintr(self):
+ # the GLOBAL_ACK_EINTR flag was introduced in libcurl-7.30, but can also
+ # be backported for older versions of libcurl at the distribution level
+ if not util.pycurl_version_less_than(7, 30) or hasattr(pycurl, 'GLOBAL_ACK_EINTR'):
+ # initialize libcurl with the GLOBAL_ACK_EINTR flag
+ pycurl.global_init(pycurl.GLOBAL_ACK_EINTR)
+ pycurl.global_cleanup()
--
1.7.1
From 788e012e64250cfb473d201a836e1a92c7772154 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Tue, 9 Apr 2013 18:52:41 -0400
Subject: [PATCH 115/149] Add a test for bogus init flags and fix test name
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/global_init_ack_eintr.py | 22 ----------------------
tests/global_init_test.py | 28 ++++++++++++++++++++++++++++
2 files changed, 28 insertions(+), 22 deletions(-)
delete mode 100644 tests/global_init_ack_eintr.py
create mode 100644 tests/global_init_test.py
diff --git a/tests/global_init_ack_eintr.py b/tests/global_init_ack_eintr.py
deleted file mode 100644
index 429fc3f..0000000
--- a/tests/global_init_ack_eintr.py
+++ /dev/null
@@ -1,22 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: iso-8859-1 -*-
-# vi:ts=4:et
-
-import pycurl
-import unittest
-
-from . import util
-
-class GlobalInitAckEintrTest(unittest.TestCase):
- def test_global_init_default(self):
- # initialize libcurl with DEFAULT flags
- pycurl.global_init(pycurl.GLOBAL_DEFAULT)
- pycurl.global_cleanup()
-
- def test_global_init_ack_eintr(self):
- # the GLOBAL_ACK_EINTR flag was introduced in libcurl-7.30, but can also
- # be backported for older versions of libcurl at the distribution level
- if not util.pycurl_version_less_than(7, 30) or hasattr(pycurl, 'GLOBAL_ACK_EINTR'):
- # initialize libcurl with the GLOBAL_ACK_EINTR flag
- pycurl.global_init(pycurl.GLOBAL_ACK_EINTR)
- pycurl.global_cleanup()
diff --git a/tests/global_init_test.py b/tests/global_init_test.py
new file mode 100644
index 0000000..b76254b
--- /dev/null
+++ b/tests/global_init_test.py
@@ -0,0 +1,28 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+import pycurl
+import unittest
+import nose.tools
+
+from . import util
+
+class GlobalInitTest(unittest.TestCase):
+ def test_global_init_default(self):
+ # initialize libcurl with DEFAULT flags
+ pycurl.global_init(pycurl.GLOBAL_DEFAULT)
+ pycurl.global_cleanup()
+
+ def test_global_init_ack_eintr(self):
+ # the GLOBAL_ACK_EINTR flag was introduced in libcurl-7.30, but can also
+ # be backported for older versions of libcurl at the distribution level
+ if not util.pycurl_version_less_than(7, 30) or hasattr(pycurl, 'GLOBAL_ACK_EINTR'):
+ # initialize libcurl with the GLOBAL_ACK_EINTR flag
+ pycurl.global_init(pycurl.GLOBAL_ACK_EINTR)
+ pycurl.global_cleanup()
+
+ @nose.tools.raises(ValueError)
+ def test_global_init_bogus(self):
+ # initialize libcurl with bogus flags
+ pycurl.global_init(0xffff)
--
1.7.1
From 91992e2ef3664d68c8e98269b78911b929ad991a Mon Sep 17 00:00:00 2001
From: anonymous <anonymous@users.sourceforge.net>
Date: Tue, 26 Feb 2013 16:32:20 -0500
Subject: [PATCH 116/149] Python 3 support.
http://sourceforge.net/tracker/?func=detail&aid=2917775&group_id=28236&atid=392779
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
setup.py | 18 +++++++++---------
1 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/setup.py b/setup.py
index 725007c..e0d0379 100644
--- a/setup.py
+++ b/setup.py
@@ -31,7 +31,7 @@ def scan_argv(s, default):
i = 1
while i < len(sys.argv):
arg = sys.argv[i]
- if string.find(arg, s) == 0:
+ if str.find(arg, s) == 0:
p = arg[len(s):]
assert p, arg
del sys.argv[i]
@@ -46,8 +46,8 @@ def add_libdirs(envvar, sep, fatal=0):
v = os.environ.get(envvar)
if not v:
return
- for dir in string.split(v, sep):
- dir = string.strip(dir)
+ for dir in str.split(v, sep):
+ dir = str.strip(dir)
if not dir:
continue
dir = os.path.normpath(dir)
@@ -55,7 +55,7 @@ def add_libdirs(envvar, sep, fatal=0):
if not dir in library_dirs:
library_dirs.append(dir)
elif fatal:
- print "FATAL: bad directory %s in environment variable %s" % (dir, envvar)
+ print("FATAL: bad directory %s in environment variable %s" % (dir, envvar))
sys.exit(1)
@@ -65,13 +65,13 @@ if sys.platform == "win32":
# and thus unlikely to match your installation.
CURL_DIR = r"c:\src\build\pycurl\curl-7.16.2.1"
CURL_DIR = scan_argv("--curl-dir=", CURL_DIR)
- print "Using curl directory:", CURL_DIR
+ print("Using curl directory:", CURL_DIR)
assert os.path.isdir(CURL_DIR), "please check CURL_DIR in setup.py"
include_dirs.append(os.path.join(CURL_DIR, "include"))
extra_objects.append(os.path.join(CURL_DIR, "lib", "libcurl.lib"))
extra_link_args.extend(["gdi32.lib", "wldap32.lib", "winmm.lib", "ws2_32.lib",])
add_libdirs("LIB", ";")
- if string.find(sys.version, "MSC") >= 0:
+ if str.find(sys.version, "MSC") >= 0:
extra_compile_args.append("-O2")
extra_compile_args.append("-GF") # enable read-only string pooling
extra_compile_args.append("-WX") # treat warnings as errors
@@ -85,10 +85,10 @@ else:
CURL_CONFIG = scan_argv("--curl-config=", CURL_CONFIG)
d = os.popen("'%s' --version" % CURL_CONFIG).read()
if d:
- d = string.strip(d)
+ d = str.strip(d)
if not d:
- raise Exception, ("`%s' not found -- please install the libcurl development files" % CURL_CONFIG)
- print "Using %s (%s)" % (CURL_CONFIG, d)
+ raise Exception("`%s' not found -- please install the libcurl development files" % CURL_CONFIG)
+ print("Using %s (%s)" % (CURL_CONFIG, d))
for e in split_quoted(os.popen("'%s' --cflags" % CURL_CONFIG).read()):
if e[:2] == "-I":
# do not add /usr/include
--
1.7.1
From 6f6649b79ea66493e878eda3e3cfd87cba18a02f Mon Sep 17 00:00:00 2001
From: decitre <decitre@users.sourceforge.net>
Date: Tue, 26 Feb 2013 16:36:32 -0500
Subject: [PATCH 117/149] Corrected Python 3 support.
http://sourceforge.net/tracker/?func=detail&aid=3188495&group_id=28236&atid=392779
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
setup.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/setup.py b/setup.py
index e0d0379..385facb 100644
--- a/setup.py
+++ b/setup.py
@@ -106,7 +106,7 @@ else:
stdout=subprocess.PIPE)
(stdout, stderr) = p.communicate()
if p.wait() == 0:
- optbuf += stdout
+ optbuf += stdout.decode()
if optbuf == "":
raise Exception("Neither of curl-config --libs or --static-libs" +
"produced output")
--
1.7.1
From d256095c74857a87fa7e3c9dbb0df58cdeded17f Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Thu, 28 Feb 2013 00:14:03 -0500
Subject: [PATCH 118/149] Python 3 compatibility: StringIO (src)
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
python/curl/__init__.py | 5 ++++-
1 files changed, 4 insertions(+), 1 deletions(-)
diff --git a/python/curl/__init__.py b/python/curl/__init__.py
index 5617262..3ab019f 100644
--- a/python/curl/__init__.py
+++ b/python/curl/__init__.py
@@ -10,7 +10,10 @@ import os, sys, urllib, exceptions, mimetools, pycurl
try:
from cStringIO import StringIO
except ImportError:
- from StringIO import StringIO
+ try:
+ from StringIO import StringIO
+ except ImportError:
+ from io import StringIO
try:
import signal
--
1.7.1
From 04ebd364f9b027e0bbdaeecfca02493244efcfbd Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Thu, 28 Feb 2013 00:11:58 -0500
Subject: [PATCH 119/149] Python 3 compatibility: StringIO (examples)
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
examples/xmlrpc_curl.py | 5 ++++-
1 files changed, 4 insertions(+), 1 deletions(-)
diff --git a/examples/xmlrpc_curl.py b/examples/xmlrpc_curl.py
index a9a7d9e..bb6799e 100644
--- a/examples/xmlrpc_curl.py
+++ b/examples/xmlrpc_curl.py
@@ -14,7 +14,10 @@ except ImportError:
try:
from cStringIO import StringIO
except ImportError:
- from StringIO import StringIO
+ try:
+ from StringIO import StringIO
+ except ImportError:
+ from io import StringIO
import xmlrpclib, pycurl
--
1.7.1
From 936cceff39402efa5b91b2ff4d4d36c04c1675c8 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Thu, 28 Feb 2013 00:23:29 -0500
Subject: [PATCH 120/149] Python 3 compatibility: xmlrpclib
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
examples/tests/test_xmlrpc.py | 5 ++++-
examples/xmlrpc_curl.py | 6 +++++-
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/examples/tests/test_xmlrpc.py b/examples/tests/test_xmlrpc.py
index 3a5469a..d64794e 100644
--- a/examples/tests/test_xmlrpc.py
+++ b/examples/tests/test_xmlrpc.py
@@ -4,7 +4,10 @@
# $Id$
## XML-RPC lib included in python2.2
-import xmlrpclib
+try:
+ import xmlrpclib
+except ImportError:
+ import xmlrpc.client as xmlrpclib
import pycurl
# Header fields passed in request
diff --git a/examples/xmlrpc_curl.py b/examples/xmlrpc_curl.py
index bb6799e..21418b5 100644
--- a/examples/xmlrpc_curl.py
+++ b/examples/xmlrpc_curl.py
@@ -18,7 +18,11 @@ except ImportError:
from StringIO import StringIO
except ImportError:
from io import StringIO
-import xmlrpclib, pycurl
+try:
+ import xmlrpclib
+except ImportError:
+ import xmlrpc.client as xmlrpclib
+import pycurl
class CURLTransport(xmlrpclib.Transport):
--
1.7.1
From 00ce4e4176540bc899713bcd2c364ad8d3887a8b Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Thu, 28 Feb 2013 00:34:01 -0500
Subject: [PATCH 121/149] Python 3 compatibility: urllib (src)
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
python/curl/__init__.py | 10 +++++++---
1 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/python/curl/__init__.py b/python/curl/__init__.py
index 3ab019f..fd179fc 100644
--- a/python/curl/__init__.py
+++ b/python/curl/__init__.py
@@ -6,7 +6,11 @@
#
# By Eric S. Raymond, April 2003.
-import os, sys, urllib, exceptions, mimetools, pycurl
+import os, sys, exceptions, mimetools, pycurl
+try:
+ import urllib.parse as urllib_parse
+except ImportError:
+ import urllib as urllib_parse
try:
from cStringIO import StringIO
except ImportError:
@@ -86,14 +90,14 @@ class Curl:
def get(self, url="", params=None):
"Ship a GET request for a specified URL, capture the response."
if params:
- url += "?" + urllib.urlencode(params)
+ url += "?" + urllib_parse.urlencode(params)
self.set_option(pycurl.HTTPGET, 1)
return self.__request(url)
def post(self, cgi, params):
"Ship a POST request to a specified CGI, capture the response."
self.set_option(pycurl.POST, 1)
- self.set_option(pycurl.POSTFIELDS, urllib.urlencode(params))
+ self.set_option(pycurl.POSTFIELDS, urllib_parse.urlencode(params))
return self.__request(cgi)
def body(self):
--
1.7.1
From 59cde00a2b6f4329d19579c3e8982d194c71eb66 Mon Sep 17 00:00:00 2001
From: Yuri Ushakov <yuri.ushakov@gmail.com>
Date: Thu, 18 Apr 2013 12:29:07 -0400
Subject: [PATCH 122/149] Correctly handle big timeout values
http://curl.haxx.se/mail/curlpython-2013-04/0000.html
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
src/pycurl.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/src/pycurl.c b/src/pycurl.c
index 9950e00..388595c 100644
--- a/src/pycurl.c
+++ b/src/pycurl.c
@@ -2567,7 +2567,7 @@ do_multi_timeout(CurlMultiObject *self)
}
/* Return number of millisecs until timeout */
- return Py_BuildValue("i", timeout);
+ return Py_BuildValue("l", timeout);
}
--
1.7.1
From 575f40dd24cea343980b0c628327d9a1008fa021 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Thu, 30 May 2013 13:55:35 -0400
Subject: [PATCH 123/149] Add support for CURLOPT_USERNAME / _PASSWORD
Original patch by Wim Lewis (https://sourceforge.net/u/wiml/profile/).
Original patch description:
This is a really trivial patch to add support for the CURLOPT_USERNAME and
CURLOPT_PASSWORD options (and their _PROXY equivalents), which affect
the same parts of libcurl's state that CURLOPT_USERPWD does but which
don't require you to combine the username and password into one string first.
(Libcurl just immediately parses it apart again anyway.)
I've tested against libcurl-7.21.0, and looked through the source for
libcurl 7.20.0 and 7.19.5 to verify that it looks like it should still do
the right things there (in particular that curl_easy_setopt(..., NULL)
does the reasonable thing with these options).
https://sourceforge.net/p/pycurl/patches/10/
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
src/pycurl.c | 31 +++++++++++++++++++++++++++++++
tests/curlopt_test.py | 18 ++++++++++++++++++
2 files changed, 49 insertions(+), 0 deletions(-)
create mode 100644 tests/curlopt_test.py
diff --git a/src/pycurl.c b/src/pycurl.c
index 388595c..9c76609 100644
--- a/src/pycurl.c
+++ b/src/pycurl.c
@@ -61,6 +61,13 @@
# error "Need libcurl version 7.19.0 or greater to compile pycurl."
#endif
+#if LIBCURL_VERSION_MAJOR >= 8 || \
+ LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR >= 20 || \
+ LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR == 19 && LIBCURL_VERSION_PATCH >= 1
+#define HAVE_CURLOPT_USERNAME
+#define HAVE_CURLOPT_PROXYUSERNAME
+#endif
+
/* Python < 2.5 compat for Py_ssize_t */
#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
typedef int Py_ssize_t;
@@ -1558,9 +1565,17 @@ util_curl_unsetopt(CurlObject *self, int option)
case CURLOPT_EGDSOCKET:
case CURLOPT_FTPPORT:
case CURLOPT_PROXYUSERPWD:
+#ifdef HAVE_CURLOPT_PROXYUSERNAME
+ case CURLOPT_PROXYUSERNAME:
+ case CURLOPT_PROXYPASSWORD:
+#endif
case CURLOPT_RANDOM_FILE:
case CURLOPT_SSL_CIPHER_LIST:
case CURLOPT_USERPWD:
+#ifdef HAVE_CURLOPT_USERNAME
+ case CURLOPT_USERNAME:
+ case CURLOPT_PASSWORD:
+#endif
case CURLOPT_RANGE:
SETOPT((char *) 0);
break;
@@ -1658,6 +1673,10 @@ do_curl_setopt(CurlObject *self, PyObject *args)
case CURLOPT_NETRC_FILE:
case CURLOPT_PROXY:
case CURLOPT_PROXYUSERPWD:
+#ifdef HAVE_CURLOPT_PROXYUSERNAME
+ case CURLOPT_PROXYUSERNAME:
+ case CURLOPT_PROXYPASSWORD:
+#endif
case CURLOPT_RANDOM_FILE:
case CURLOPT_RANGE:
case CURLOPT_REFERER:
@@ -1671,6 +1690,10 @@ do_curl_setopt(CurlObject *self, PyObject *args)
case CURLOPT_URL:
case CURLOPT_USERAGENT:
case CURLOPT_USERPWD:
+#ifdef HAVE_CURLOPT_USERNAME
+ case CURLOPT_USERNAME:
+ case CURLOPT_PASSWORD:
+#endif
case CURLOPT_FTP_ALTERNATIVE_TO_USER:
case CURLOPT_SSH_PUBLIC_KEYFILE:
case CURLOPT_SSH_PRIVATE_KEYFILE:
@@ -3654,7 +3677,15 @@ initpycurl(void)
insint_c(d, "PORT", CURLOPT_PORT);
insint_c(d, "PROXY", CURLOPT_PROXY);
insint_c(d, "USERPWD", CURLOPT_USERPWD);
+#ifdef HAVE_CURLOPT_USERNAME
+ insint_c(d, "USERNAME", CURLOPT_USERNAME);
+ insint_c(d, "PASSWORD", CURLOPT_PASSWORD);
+#endif
insint_c(d, "PROXYUSERPWD", CURLOPT_PROXYUSERPWD);
+#ifdef HAVE_CURLOPT_PROXYUSERNAME
+ insint_c(d, "PROXYUSERNAME", CURLOPT_PROXYUSERNAME);
+ insint_c(d, "PROXYPASSWORD", CURLOPT_PROXYPASSWORD);
+#endif
insint_c(d, "RANGE", CURLOPT_RANGE);
insint_c(d, "INFILE", CURLOPT_READDATA);
/* ERRORBUFFER is not supported */
diff --git a/tests/curlopt_test.py b/tests/curlopt_test.py
new file mode 100644
index 0000000..9392a39
--- /dev/null
+++ b/tests/curlopt_test.py
@@ -0,0 +1,18 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+import pycurl
+import unittest
+import nose.tools
+
+from . import util
+
+class CurloptTest(unittest.TestCase):
+ def test_username(self):
+ # CURLOPT_USERNAME was introduced in libcurl-7.19.1
+ if not util.pycurl_version_less_than(7, 19, 1):
+ assert hasattr(pycurl, 'USERNAME')
+ assert hasattr(pycurl, 'PASSWORD')
+ assert hasattr(pycurl, 'PROXYUSERNAME')
+ assert hasattr(pycurl, 'PROXYPASSWORD')
--
1.7.1
From db2f9a10ef399f0a69b2e4a215fe44752f780b96 Mon Sep 17 00:00:00 2001
From: Adam Guthrie <therigu@users.sourceforge.net>
Date: Thu, 30 May 2013 14:10:44 -0400
Subject: [PATCH 124/149] Fixes invalid XHTML in documentation
doc/callbacks.html and doc/curlshareobject.html were not valid xhtml.
Thanks to Ivo Timmermans for the original report and patch to Ubuntu (LP: #364168)
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
doc/callbacks.html | 2 +-
doc/curlshareobject.html | 3 ++-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/doc/callbacks.html b/doc/callbacks.html
index b98e0a9..a117acc 100644
--- a/doc/callbacks.html
+++ b/doc/callbacks.html
@@ -120,7 +120,7 @@ VERBOSE option must be enabled for this callback to be invoked.</p>
</pre>
<h2>Other examples</h2>
-The pycurl distribution also contains a number of test scripts and
+<p>The pycurl distribution also contains a number of test scripts and
examples which show how to use the various callbacks in libcurl.
For instance, the file 'examples/file_upload.py' in the distribution contains
example code for using READFUNCTION, 'tests/test_cb.py' shows
diff --git a/doc/curlshareobject.html b/doc/curlshareobject.html
index c11cafb..2043e48 100644
--- a/doc/curlshareobject.html
+++ b/doc/curlshareobject.html
@@ -14,6 +14,7 @@
<p>CurlShare objects have the following methods:</p>
+<dl>
<dt><code>setopt(</code><em>option, value</em><code>)</code> -&gt; <em>None</em></dt>
<dd>
@@ -22,7 +23,7 @@
href="http://curl.haxx.se/libcurl/c/curl_share_setopt.html"><code>curl_share_setopt</code></a> in libcurl, where
<em>option</em> is specified with the CURLSHOPT_* constants in libcurl,
except that the CURLSHOPT_ prefix has been changed to SH_. Currently,
-<em>value</em> must be either LOCK_DATA_COOKIE or LOCK_DATA_DNS.
+<em>value</em> must be either LOCK_DATA_COOKIE or LOCK_DATA_DNS.</p>
<p>Example usage:</p>
--
1.7.1
From 87aa7c722ddfefd38faef5a8a038c2e8e64db003 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Thu, 30 May 2013 15:27:02 -0400
Subject: [PATCH 125/149] Use nose test skipping functionality
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/curlopt_test.py | 14 ++++++++------
tests/global_init_test.py | 11 +++++++----
2 files changed, 15 insertions(+), 10 deletions(-)
diff --git a/tests/curlopt_test.py b/tests/curlopt_test.py
index 9392a39..fd30394 100644
--- a/tests/curlopt_test.py
+++ b/tests/curlopt_test.py
@@ -4,15 +4,17 @@
import pycurl
import unittest
-import nose.tools
+import nose.plugins.skip
from . import util
class CurloptTest(unittest.TestCase):
def test_username(self):
# CURLOPT_USERNAME was introduced in libcurl-7.19.1
- if not util.pycurl_version_less_than(7, 19, 1):
- assert hasattr(pycurl, 'USERNAME')
- assert hasattr(pycurl, 'PASSWORD')
- assert hasattr(pycurl, 'PROXYUSERNAME')
- assert hasattr(pycurl, 'PROXYPASSWORD')
+ if util.pycurl_version_less_than(7, 19, 1):
+ raise nose.plugins.skip.SkipTest('libcurl < 7.19.1')
+
+ assert hasattr(pycurl, 'USERNAME')
+ assert hasattr(pycurl, 'PASSWORD')
+ assert hasattr(pycurl, 'PROXYUSERNAME')
+ assert hasattr(pycurl, 'PROXYPASSWORD')
diff --git a/tests/global_init_test.py b/tests/global_init_test.py
index b76254b..443ca49 100644
--- a/tests/global_init_test.py
+++ b/tests/global_init_test.py
@@ -5,6 +5,7 @@
import pycurl
import unittest
import nose.tools
+import nose.plugins.skip
from . import util
@@ -17,10 +18,12 @@ class GlobalInitTest(unittest.TestCase):
def test_global_init_ack_eintr(self):
# the GLOBAL_ACK_EINTR flag was introduced in libcurl-7.30, but can also
# be backported for older versions of libcurl at the distribution level
- if not util.pycurl_version_less_than(7, 30) or hasattr(pycurl, 'GLOBAL_ACK_EINTR'):
- # initialize libcurl with the GLOBAL_ACK_EINTR flag
- pycurl.global_init(pycurl.GLOBAL_ACK_EINTR)
- pycurl.global_cleanup()
+ if util.pycurl_version_less_than(7, 30) or hasattr(pycurl, 'GLOBAL_ACK_EINTR'):
+ raise nose.plugins.skip.SkipTest('libcurl < 7.30.0 or no GLOBAL_ACK_EINTR')
+
+ # initialize libcurl with the GLOBAL_ACK_EINTR flag
+ pycurl.global_init(pycurl.GLOBAL_ACK_EINTR)
+ pycurl.global_cleanup()
@nose.tools.raises(ValueError)
def test_global_init_bogus(self):
--
1.7.1
From 7e8144d0d83062de394582207197e1081eafcc24 Mon Sep 17 00:00:00 2001
From: Wim Lewis <wiml@users.sourceforge.net>
Date: Thu, 21 Jul 2011 12:00:00 -0400
Subject: [PATCH 126/149] Fix a tiny memory leak in util_curl_init()
Each time a new Curl object is created util_curl_init() leaked
a small string buffer. The attached patch fixes the leak --
curl_easy_setopt() strdup's its argument; the caller does not need
to keep the buffer around for it.
https://sourceforge.net/p/pycurl/patches/13/
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
src/pycurl.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/src/pycurl.c b/src/pycurl.c
index 9c76609..ef03747 100644
--- a/src/pycurl.c
+++ b/src/pycurl.c
@@ -793,8 +793,8 @@ util_curl_init(CurlObject *self)
}
strcpy(s, "PycURL/"); strcpy(s+7, LIBCURL_VERSION);
res = curl_easy_setopt(self->handle, CURLOPT_USERAGENT, (char *) s);
+ free(s);
if (res != CURLE_OK) {
- free(s);
return (-1);
}
return (0);
--
1.7.1
From 14e3edf2f77368af4f3838202527c8a48a359d6a Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Fri, 31 May 2013 13:37:39 -0400
Subject: [PATCH 127/149] Use correct logic when checking for GLOBAL_ACK_EINTR (#34)
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/global_init_test.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/tests/global_init_test.py b/tests/global_init_test.py
index 443ca49..b0d1986 100644
--- a/tests/global_init_test.py
+++ b/tests/global_init_test.py
@@ -18,7 +18,7 @@ class GlobalInitTest(unittest.TestCase):
def test_global_init_ack_eintr(self):
# the GLOBAL_ACK_EINTR flag was introduced in libcurl-7.30, but can also
# be backported for older versions of libcurl at the distribution level
- if util.pycurl_version_less_than(7, 30) or hasattr(pycurl, 'GLOBAL_ACK_EINTR'):
+ if util.pycurl_version_less_than(7, 30) and not hasattr(pycurl, 'GLOBAL_ACK_EINTR'):
raise nose.plugins.skip.SkipTest('libcurl < 7.30.0 or no GLOBAL_ACK_EINTR')
# initialize libcurl with the GLOBAL_ACK_EINTR flag
--
1.7.1
From b2157864dc9b80222d04e6a681d923c1bdc6c64f Mon Sep 17 00:00:00 2001
From: Roland Sommer <rol@ndsommer.de>
Date: Wed, 8 May 2013 15:13:14 +0300
Subject: [PATCH 128/149] Use urlparse.urljoin instead of os.path.join
Using a base_url like "http://www.google.de" and calling
get("/") on a curl.Curl-instance does not work.
os.path.join("http://www.google.de", "/") does not yield
the expected "http://www.google.de/" but "/" which leads to:
error: (3, '<url> malformed')
Using urljoin fixes this.
https://github.com/christophwarner/PyCurl/pull/2
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
python/curl/__init__.py | 6 ++++--
1 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/python/curl/__init__.py b/python/curl/__init__.py
index fd179fc..31b9d49 100644
--- a/python/curl/__init__.py
+++ b/python/curl/__init__.py
@@ -6,11 +6,13 @@
#
# By Eric S. Raymond, April 2003.
-import os, sys, exceptions, mimetools, pycurl
+import sys, exceptions, mimetools, pycurl
try:
import urllib.parse as urllib_parse
+ from urllib.parse import urljoin
except ImportError:
import urllib as urllib_parse
+ from urlparse import urljoin
try:
from cStringIO import StringIO
except ImportError:
@@ -81,7 +83,7 @@ class Curl:
if self.fakeheaders:
self.set_option(pycurl.HTTPHEADER, self.fakeheaders)
if relative_url:
- self.set_option(pycurl.URL,os.path.join(self.base_url,relative_url))
+ self.set_option(pycurl.URL, urljoin(self.base_url, relative_url))
self.payload = ""
self.hdr = ""
self.handle.perform()
--
1.7.1
From b12be12fc45fc786e2d537a3bbd3044488f1e367 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Tue, 4 Jun 2013 13:30:39 -0400
Subject: [PATCH 129/149] Test coverage for relative urls in curl module
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/relative_url_test.py | 24 ++++++++++++++++++++++++
1 files changed, 24 insertions(+), 0 deletions(-)
create mode 100644 tests/relative_url_test.py
diff --git a/tests/relative_url_test.py b/tests/relative_url_test.py
new file mode 100644
index 0000000..ddff2b6
--- /dev/null
+++ b/tests/relative_url_test.py
@@ -0,0 +1,24 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+# uses the high level interface
+import curl
+import unittest
+
+from . import app
+from . import runwsgi
+from . import util
+
+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380))
+
+class RelativeUrlTest(unittest.TestCase):
+ def setUp(self):
+ self.curl = curl.Curl('http://localhost:8380/')
+
+ def tearDown(self):
+ self.curl.close()
+
+ def test_get_relative(self):
+ self.curl.get('/success')
+ self.assertEqual('success', self.curl.body())
--
1.7.1
From d29d339d892c52aad6e0be0b22b75dc52e9d9450 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Sun, 2 Jun 2013 01:57:16 -0400
Subject: [PATCH 130/149] Avoid dynamically allocating memory for default pycurl user agent.
This implementation requires LIBCURL_VERSION to be a #define.
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
src/pycurl.c | 11 ++++-------
1 files changed, 4 insertions(+), 7 deletions(-)
diff --git a/src/pycurl.c b/src/pycurl.c
index ef03747..b9d797e 100644
--- a/src/pycurl.c
+++ b/src/pycurl.c
@@ -105,6 +105,9 @@ static void pycurl_ssl_cleanup(void);
#define OPTIONS_SIZE ((int)CURLOPT_LASTENTRY % 10000)
#define MOPTIONS_SIZE ((int)CURLMOPT_LASTENTRY % 10000)
+/* Keep some default variables around */
+static char *g_pycurl_useragent = "PycURL/" LIBCURL_VERSION;
+
/* Type objects */
static PyObject *ErrorObject = NULL;
static PyTypeObject *p_Curl_Type = NULL;
@@ -787,13 +790,7 @@ util_curl_init(CurlObject *self)
}
/* Set default USERAGENT */
- s = (char *) malloc(7 + strlen(LIBCURL_VERSION) + 1);
- if (s == NULL) {
- return (-1);
- }
- strcpy(s, "PycURL/"); strcpy(s+7, LIBCURL_VERSION);
- res = curl_easy_setopt(self->handle, CURLOPT_USERAGENT, (char *) s);
- free(s);
+ res = curl_easy_setopt(self->handle, CURLOPT_USERAGENT, (char *) g_pycurl_useragent);
if (res != CURLE_OK) {
return (-1);
}
--
1.7.1
From f8d2f64764ee25572453317371ff58fe048a8a8a Mon Sep 17 00:00:00 2001
From: Marien Zwart <marienz@users.sourceforge.net>
Date: Wed, 13 Jun 2012 00:00:00 -0400
Subject: [PATCH 131/149] Mark NSS as supported.
Mark NSS as supported, as it does not require the application to
initialize threading.
If curl is using nss we do not seem to need to initialize threading
explicitly: the only threading-related bit I see in the nss
headers/documentation has to do with simultaneous non-nss use of
pkcs11 modules, nss itself seems to be thread-safe by default.
So silence the build warning.
http://sourceforge.net/p/pycurl/patches/15/
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
setup.py | 2 ++
src/pycurl.c | 4 ++--
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/setup.py b/setup.py
index 385facb..40da07a 100644
--- a/setup.py
+++ b/setup.py
@@ -119,6 +119,8 @@ else:
define_macros.append(('HAVE_CURL_OPENSSL', 1))
if e[2:] == 'gnutls':
define_macros.append(('HAVE_CURL_GNUTLS', 1))
+ if e[2:] == 'ssl3':
+ define_macros.append(('HAVE_CURL_NSS', 1))
elif e[:2] == "-L":
library_dirs.append(e[2:])
else:
diff --git a/src/pycurl.c b/src/pycurl.c
index b9d797e..a913c47 100644
--- a/src/pycurl.c
+++ b/src/pycurl.c
@@ -88,12 +88,12 @@ typedef int Py_ssize_t;
# define PYCURL_NEED_SSL_TSL
# define PYCURL_NEED_GNUTLS_TSL
# include <gcrypt.h>
-# else
+# elif !defined(HAVE_CURL_NSS)
# warning \
"libcurl was compiled with SSL support, but configure could not determine which " \
"library was used; thus no SSL crypto locking callbacks will be set, which may " \
"cause random crashes on SSL requests"
-# endif /* HAVE_CURL_OPENSSL || HAVE_CURL_GNUTLS */
+# endif /* HAVE_CURL_OPENSSL || HAVE_CURL_GNUTLS || HAVE_CURL_NSS */
#endif /* HAVE_CURL_SSL */
#if defined(PYCURL_NEED_SSL_TSL)
--
1.7.1
From 256f4bece80ee579daf18cbccc6007c0fa517903 Mon Sep 17 00:00:00 2001
From: Marien Zwart <marienz@users.sourceforge.net>
Date: Wed, 13 Jun 2012 00:00:00 -0400
Subject: [PATCH 132/149] Only initialize gcrypt if we are using an older gnutls that needs this.
This is necessary to support newer gnutls linked to libnettle instead
of libgcrypt.
If curl is using a recent version of gnutls we do not need to initialize
libgcrypt threading: gnutls does that for us. And because recent versions
of gnutls may be using nettle instead of gcrypt it is important that we
don't: those functions are not necessarily defined (and linking to
libgcrypt explicitly to get them is stupid, as they're not used). The
modification is based on instructions from the gnutls NEWS file.
http://sourceforge.net/p/pycurl/patches/15/
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
src/pycurl.c | 9 ++++++---
1 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/pycurl.c b/src/pycurl.c
index a913c47..5d4e5b0 100644
--- a/src/pycurl.c
+++ b/src/pycurl.c
@@ -85,9 +85,12 @@ typedef int Py_ssize_t;
# define PYCURL_NEED_OPENSSL_TSL
# include <openssl/crypto.h>
# elif defined(HAVE_CURL_GNUTLS)
-# define PYCURL_NEED_SSL_TSL
-# define PYCURL_NEED_GNUTLS_TSL
-# include <gcrypt.h>
+# include <gnutls/gnutls.h>
+# if GNUTLS_VERSION_NUMBER <= 0x020b00
+# define PYCURL_NEED_SSL_TSL
+# define PYCURL_NEED_GNUTLS_TSL
+# include <gcrypt.h>
+# endif
# elif !defined(HAVE_CURL_NSS)
# warning \
"libcurl was compiled with SSL support, but configure could not determine which " \
--
1.7.1
From f4ec6883c9d5ebfa961aa6063313903b2ca7ce1e Mon Sep 17 00:00:00 2001
From: Romulo A. Ceccon <romuloceccon@gmail.com>
Date: Sun, 9 Jun 2013 17:34:41 +0000
Subject: [PATCH 133/149] Allow pycURL to be used with Python binaries built without thread support
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
src/pycurl.c | 215 ++++++++++++++++++++++++++++++++++++++-------------------
1 files changed, 143 insertions(+), 72 deletions(-)
diff --git a/src/pycurl.c b/src/pycurl.c
index 5d4e5b0..a9923ed 100644
--- a/src/pycurl.c
+++ b/src/pycurl.c
@@ -104,6 +104,30 @@ static void pycurl_ssl_init(void);
static void pycurl_ssl_cleanup(void);
#endif
+#ifdef WITH_THREAD
+# define PYCURL_DECLARE_THREAD_STATE PyThreadState *tmp_state
+# define PYCURL_ACQUIRE_THREAD() acquire_thread(self, &tmp_state)
+# define PYCURL_ACQUIRE_THREAD_MULTI() acquire_thread_multi(self, &tmp_state)
+# define PYCURL_RELEASE_THREAD() release_thread(tmp_state)
+/* Replacement for Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS when python
+ callbacks are expected during blocking i/o operations: self->state will hold
+ the handle to current thread to be used as context */
+# define PYCURL_BEGIN_ALLOW_THREADS \
+ self->state = PyThreadState_Get(); \
+ assert(self->state != NULL); \
+ Py_BEGIN_ALLOW_THREADS
+# define PYCURL_END_ALLOW_THREADS \
+ Py_END_ALLOW_THREADS \
+ self->state = NULL;
+#else
+# define PYCURL_DECLARE_THREAD_STATE
+# define PYCURL_ACQUIRE_THREAD() (1)
+# define PYCURL_ACQUIRE_THREAD_MULTI() (1)
+# define PYCURL_RELEASE_THREAD()
+# define PYCURL_BEGIN_ALLOW_THREADS
+# define PYCURL_END_ALLOW_THREADS
+#endif
+
/* Calculate the number of OBJECTPOINT options we need to store */
#define OPTIONS_SIZE ((int)CURLOPT_LASTENTRY % 10000)
#define MOPTIONS_SIZE ((int)CURLMOPT_LASTENTRY % 10000)
@@ -126,14 +150,18 @@ typedef struct {
PyObject_HEAD
PyObject *dict; /* Python attributes dictionary */
CURLSH *share_handle;
+#ifdef WITH_THREAD
ShareLock *lock; /* lock object to implement CURLSHOPT_LOCKFUNC */
+#endif
} CurlShareObject;
typedef struct {
PyObject_HEAD
PyObject *dict; /* Python attributes dictionary */
CURLM *multi_handle;
+#ifdef WITH_THREAD
PyThreadState *state;
+#endif
fd_set read_fd_set;
fd_set write_fd_set;
fd_set exc_fd_set;
@@ -146,7 +174,9 @@ typedef struct {
PyObject_HEAD
PyObject *dict; /* Python attributes dictionary */
CURL *handle;
+#ifdef WITH_THREAD
PyThreadState *state;
+#endif
CurlMultiObject *multi_stack;
CurlShareObject *share;
struct curl_httppost *httppost;
@@ -261,6 +291,7 @@ error:
}
+#ifdef WITH_THREAD
/*************************************************************************
// static utility functions
**************************************************************************/
@@ -317,13 +348,42 @@ get_thread_state_multi(const CurlMultiObject *self)
}
+static int acquire_thread(const CurlObject *self, PyThreadState **state)
+{
+ *state = get_thread_state(self);
+ if (*state == NULL)
+ return 0;
+ PyEval_AcquireThread(*state);
+ return 1;
+}
+
+
+static int acquire_thread_multi(const CurlMultiObject *self, PyThreadState **state)
+{
+ *state = get_thread_state_multi(self);
+ if (*state == NULL)
+ return 0;
+ PyEval_AcquireThread(*state);
+ return 1;
+}
+
+
+static void release_thread(PyThreadState *state)
+{
+ PyEval_ReleaseThread(state);
+}
+#endif
+
+
/* assert some CurlShareObject invariants */
static void
assert_share_state(const CurlShareObject *self)
{
assert(self != NULL);
assert(self->ob_type == p_CurlShare_Type);
+#ifdef WITH_THREAD
assert(self->lock != NULL);
+#endif
}
@@ -333,7 +393,9 @@ assert_curl_state(const CurlObject *self)
{
assert(self != NULL);
assert(self->ob_type == p_Curl_Type);
+#ifdef WITH_THREAD
(void) get_thread_state(self);
+#endif
}
@@ -343,9 +405,11 @@ assert_multi_state(const CurlMultiObject *self)
{
assert(self != NULL);
assert(self->ob_type == p_CurlMulti_Type);
+#ifdef WITH_THREAD
if (self->state != NULL) {
assert(self->multi_handle != NULL);
}
+#endif
}
@@ -358,10 +422,12 @@ check_curl_state(const CurlObject *self, int flags, const char *name)
PyErr_Format(ErrorObject, "cannot invoke %s() - no curl handle", name);
return -1;
}
+#ifdef WITH_THREAD
if ((flags & 2) && get_thread_state(self) != NULL) {
PyErr_Format(ErrorObject, "cannot invoke %s() - perform() is currently running", name);
return -1;
}
+#endif
return 0;
}
@@ -373,10 +439,12 @@ check_multi_state(const CurlMultiObject *self, int flags, const char *name)
PyErr_Format(ErrorObject, "cannot invoke %s() - no multi handle", name);
return -1;
}
+#ifdef WITH_THREAD
if ((flags & 2) && self->state != NULL) {
PyErr_Format(ErrorObject, "cannot invoke %s() - multi_perform() is currently running", name);
return -1;
}
+#endif
return 0;
}
@@ -391,6 +459,7 @@ check_share_state(const CurlShareObject *self, int flags, const char *name)
// SSL TSL
**************************************************************************/
+#ifdef WITH_THREAD
#ifdef PYCURL_NEED_OPENSSL_TSL
static PyThread_type_lock *pycurl_openssl_tsl = NULL;
@@ -560,6 +629,19 @@ share_unlock_callback(CURL *handle, curl_lock_data data, void *userptr)
share_lock_unlock(share->lock, data);
}
+#else /* WITH_THREAD */
+
+static void pycurl_ssl_init(void)
+{
+ return;
+}
+
+static void pycurl_ssl_cleanup(void)
+{
+ return;
+}
+
+#endif /* WITH_THREAD */
/* constructor - this is a module-level function returning a new instance */
static CurlShareObject *
@@ -567,8 +649,10 @@ do_share_new(PyObject *dummy)
{
int res;
CurlShareObject *self;
+#ifdef WITH_THREAD
const curl_lock_function lock_cb = share_lock_callback;
const curl_unlock_function unlock_cb = share_unlock_callback;
+#endif
UNUSED(dummy);
@@ -583,8 +667,10 @@ do_share_new(PyObject *dummy)
/* Initialize object attributes */
self->dict = NULL;
+#ifdef WITH_THREAD
self->lock = share_lock_new();
assert(self->lock != NULL);
+#endif
/* Allocate libcurl share handle */
self->share_handle = curl_share_init();
@@ -594,6 +680,7 @@ do_share_new(PyObject *dummy)
return NULL;
}
+#ifdef WITH_THREAD
/* Set locking functions and data */
res = curl_share_setopt(self->share_handle, CURLSHOPT_LOCKFUNC, lock_cb);
assert(res == CURLE_OK);
@@ -601,6 +688,7 @@ do_share_new(PyObject *dummy)
assert(res == CURLE_OK);
res = curl_share_setopt(self->share_handle, CURLSHOPT_UNLOCKFUNC, unlock_cb);
assert(res == CURLE_OK);
+#endif
return self;
}
@@ -632,7 +720,9 @@ do_share_clear(CurlShareObject *self)
static void
util_share_close(CurlShareObject *self){
curl_share_cleanup(self->share_handle);
+#ifdef WITH_THREAD
share_lock_destroy(self->lock);
+#endif
}
@@ -723,7 +813,9 @@ util_curl_new(void)
/* Set python curl object initial values */
self->dict = NULL;
self->handle = NULL;
+#ifdef WITH_THREAD
self->state = NULL;
+#endif
self->share = NULL;
self->multi_stack = NULL;
self->httppost = NULL;
@@ -898,12 +990,16 @@ util_curl_close(CurlObject *self)
if (handle == NULL) {
/* Some paranoia assertions just to make sure the object
* deallocation problem is finally really fixed... */
+#ifdef WITH_THREAD
assert(self->state == NULL);
+#endif
assert(self->multi_stack == NULL);
assert(self->share == NULL);
return; /* already closed */
}
+#ifdef WITH_THREAD
self->state = NULL;
+#endif
/* Decref multi stuff which uses this handle */
util_curl_xdecref(self, 2, handle);
@@ -977,7 +1073,9 @@ do_curl_errstr(CurlObject *self)
static int
do_curl_clear(CurlObject *self)
{
+#ifdef WITH_THREAD
assert(get_thread_state(self) == NULL);
+#endif
util_curl_xdecref(self, 1 | 2 | 4 | 8 | 16, self->handle);
return 0;
}
@@ -1020,17 +1118,9 @@ do_curl_perform(CurlObject *self)
return NULL;
}
- /* Save handle to current thread (used as context for python callbacks) */
- self->state = PyThreadState_Get();
- assert(self->state != NULL);
-
- /* Release global lock and start */
- Py_BEGIN_ALLOW_THREADS
+ PYCURL_BEGIN_ALLOW_THREADS
res = curl_easy_perform(self->handle);
- Py_END_ALLOW_THREADS
-
- /* Zero thread-state to disallow callbacks to be run from now on */
- self->state = NULL;
+ PYCURL_END_ALLOW_THREADS
if (res != CURLE_OK) {
CURLERROR_RETVAL();
@@ -1050,7 +1140,7 @@ static size_t
util_write_callback(int flags, char *ptr, size_t size, size_t nmemb, void *stream)
{
CurlObject *self;
- PyThreadState *tmp_state;
+ PYCURL_DECLARE_THREAD_STATE;
PyObject *arglist;
PyObject *result = NULL;
size_t ret = 0; /* assume error */
@@ -1059,10 +1149,8 @@ util_write_callback(int flags, char *ptr, size_t size, size_t nmemb, void *strea
/* acquire thread */
self = (CurlObject *)stream;
- tmp_state = get_thread_state(self);
- if (tmp_state == NULL)
+ if (!PYCURL_ACQUIRE_THREAD())
return ret;
- PyEval_AcquireThread(tmp_state);
/* check args */
cb = flags ? self->h_cb : self->w_cb;
@@ -1101,7 +1189,7 @@ util_write_callback(int flags, char *ptr, size_t size, size_t nmemb, void *strea
done:
silent_error:
Py_XDECREF(result);
- PyEval_ReleaseThread(tmp_state);
+ PYCURL_RELEASE_THREAD();
return ret;
verbose_error:
PyErr_Print();
@@ -1131,13 +1219,12 @@ opensocket_callback(void *clientp, curlsocktype purpose,
PyObject *result = NULL;
PyObject *fileno_result = NULL;
CurlObject *self;
- PyThreadState *tmp_state;
+ PYCURL_DECLARE_THREAD_STATE;
int ret = CURL_SOCKET_BAD;
self = (CurlObject *)clientp;
- tmp_state = get_thread_state(self);
+ PYCURL_ACQUIRE_THREAD();
- PyEval_AcquireThread(tmp_state);
arglist = Py_BuildValue("(iii)", address->family, address->socktype, address->protocol);
if (arglist == NULL)
goto verbose_error;
@@ -1171,7 +1258,7 @@ silent_error:
done:
Py_XDECREF(result);
Py_XDECREF(fileno_result);
- PyEval_ReleaseThread(tmp_state);
+ PYCURL_RELEASE_THREAD();
return ret;
verbose_error:
PyErr_Print();
@@ -1182,7 +1269,7 @@ static int
seek_callback(void *stream, curl_off_t offset, int origin)
{
CurlObject *self;
- PyThreadState *tmp_state;
+ PYCURL_DECLARE_THREAD_STATE;
PyObject *arglist;
PyObject *result = NULL;
int ret = 2; /* assume error 2 (can't seek, libcurl free to work around). */
@@ -1191,10 +1278,8 @@ seek_callback(void *stream, curl_off_t offset, int origin)
/* acquire thread */
self = (CurlObject *)stream;
- tmp_state = get_thread_state(self);
- if (tmp_state == NULL)
+ if (!PYCURL_ACQUIRE_THREAD())
return ret;
- PyEval_AcquireThread(tmp_state);
/* check arguments */
switch (origin)
@@ -1244,7 +1329,7 @@ seek_callback(void *stream, curl_off_t offset, int origin)
silent_error:
Py_XDECREF(result);
- PyEval_ReleaseThread(tmp_state);
+ PYCURL_RELEASE_THREAD();
return ret;
verbose_error:
PyErr_Print();
@@ -1258,7 +1343,7 @@ static size_t
read_callback(char *ptr, size_t size, size_t nmemb, void *stream)
{
CurlObject *self;
- PyThreadState *tmp_state;
+ PYCURL_DECLARE_THREAD_STATE;
PyObject *arglist;
PyObject *result = NULL;
@@ -1267,10 +1352,8 @@ read_callback(char *ptr, size_t size, size_t nmemb, void *stream)
/* acquire thread */
self = (CurlObject *)stream;
- tmp_state = get_thread_state(self);
- if (tmp_state == NULL)
+ if (!PYCURL_ACQUIRE_THREAD())
return ret;
- PyEval_AcquireThread(tmp_state);
/* check args */
if (self->r_cb == NULL)
@@ -1328,7 +1411,7 @@ read_callback(char *ptr, size_t size, size_t nmemb, void *stream)
done:
silent_error:
Py_XDECREF(result);
- PyEval_ReleaseThread(tmp_state);
+ PYCURL_RELEASE_THREAD();
return ret;
verbose_error:
PyErr_Print();
@@ -1341,17 +1424,15 @@ progress_callback(void *stream,
double dltotal, double dlnow, double ultotal, double ulnow)
{
CurlObject *self;
- PyThreadState *tmp_state;
+ PYCURL_DECLARE_THREAD_STATE;
PyObject *arglist;
PyObject *result = NULL;
int ret = 1; /* assume error */
/* acquire thread */
self = (CurlObject *)stream;
- tmp_state = get_thread_state(self);
- if (tmp_state == NULL)
+ if (!PYCURL_ACQUIRE_THREAD())
return ret;
- PyEval_AcquireThread(tmp_state);
/* check args */
if (self->pro_cb == NULL)
@@ -1379,7 +1460,7 @@ progress_callback(void *stream,
silent_error:
Py_XDECREF(result);
- PyEval_ReleaseThread(tmp_state);
+ PYCURL_RELEASE_THREAD();
return ret;
verbose_error:
PyErr_Print();
@@ -1392,7 +1473,7 @@ debug_callback(CURL *curlobj, curl_infotype type,
char *buffer, size_t total_size, void *stream)
{
CurlObject *self;
- PyThreadState *tmp_state;
+ PYCURL_DECLARE_THREAD_STATE;
PyObject *arglist;
PyObject *result = NULL;
int ret = 0; /* always success */
@@ -1401,10 +1482,8 @@ debug_callback(CURL *curlobj, curl_infotype type,
/* acquire thread */
self = (CurlObject *)stream;
- tmp_state = get_thread_state(self);
- if (tmp_state == NULL)
+ if (!PYCURL_ACQUIRE_THREAD())
return ret;
- PyEval_AcquireThread(tmp_state);
/* check args */
if (self->debug_cb == NULL)
@@ -1427,7 +1506,7 @@ debug_callback(CURL *curlobj, curl_infotype type,
silent_error:
Py_XDECREF(result);
- PyEval_ReleaseThread(tmp_state);
+ PYCURL_RELEASE_THREAD();
return ret;
verbose_error:
PyErr_Print();
@@ -1439,7 +1518,7 @@ static curlioerr
ioctl_callback(CURL *curlobj, int cmd, void *stream)
{
CurlObject *self;
- PyThreadState *tmp_state;
+ PYCURL_DECLARE_THREAD_STATE;
PyObject *arglist;
PyObject *result = NULL;
int ret = CURLIOE_FAILRESTART; /* assume error */
@@ -1448,10 +1527,8 @@ ioctl_callback(CURL *curlobj, int cmd, void *stream)
/* acquire thread */
self = (CurlObject *)stream;
- tmp_state = get_thread_state(self);
- if (tmp_state == NULL)
+ if (!PYCURL_ACQUIRE_THREAD())
return (curlioerr) ret;
- PyEval_AcquireThread(tmp_state);
/* check args */
if (self->ioctl_cb == NULL)
@@ -1480,7 +1557,7 @@ ioctl_callback(CURL *curlobj, int cmd, void *stream)
silent_error:
Py_XDECREF(result);
- PyEval_ReleaseThread(tmp_state);
+ PYCURL_RELEASE_THREAD();
return (curlioerr) ret;
verbose_error:
PyErr_Print();
@@ -2324,7 +2401,9 @@ do_multi_new(PyObject *dummy)
/* Initialize object attributes */
self->dict = NULL;
+#ifdef WITH_THREAD
self->state = NULL;
+#endif
self->t_cb = NULL;
self->s_cb = NULL;
@@ -2342,7 +2421,9 @@ static void
util_multi_close(CurlMultiObject *self)
{
assert(self != NULL);
+#ifdef WITH_THREAD
self->state = NULL;
+#endif
if (self->multi_handle != NULL) {
CURLM *multi_handle = self->multi_handle;
self->multi_handle = NULL;
@@ -2411,7 +2492,7 @@ int multi_socket_callback(CURL *easy,
{
CurlMultiObject *self;
CurlObject *easy_self;
- PyThreadState *tmp_state;
+ PYCURL_DECLARE_THREAD_STATE;
PyObject *arglist;
PyObject *result = NULL;
int ret;
@@ -2419,10 +2500,8 @@ int multi_socket_callback(CURL *easy,
/* acquire thread */
self = (CurlMultiObject *)userp;
ret = curl_easy_getinfo(easy, CURLINFO_PRIVATE, &easy_self);
- tmp_state = get_thread_state_multi(self);
- if (tmp_state == NULL)
+ if (!PYCURL_ACQUIRE_THREAD_MULTI())
return 0;
- PyEval_AcquireThread(tmp_state);
/* check args */
if (self->s_cb == NULL)
@@ -2446,7 +2525,7 @@ int multi_socket_callback(CURL *easy,
silent_error:
Py_XDECREF(result);
- PyEval_ReleaseThread(tmp_state);
+ PYCURL_RELEASE_THREAD();
return 0;
verbose_error:
PyErr_Print();
@@ -2460,7 +2539,7 @@ int multi_timer_callback(CURLM *multi,
void *userp)
{
CurlMultiObject *self;
- PyThreadState *tmp_state;
+ PYCURL_DECLARE_THREAD_STATE;
PyObject *arglist;
PyObject *result = NULL;
int ret = 0; /* always success */
@@ -2469,10 +2548,8 @@ int multi_timer_callback(CURLM *multi,
/* acquire thread */
self = (CurlMultiObject *)userp;
- tmp_state = get_thread_state_multi(self);
- if (tmp_state == NULL)
+ if (!PYCURL_ACQUIRE_THREAD_MULTI())
return ret;
- PyEval_AcquireThread(tmp_state);
/* check args */
if (self->t_cb == NULL)
@@ -2491,7 +2568,7 @@ int multi_timer_callback(CURLM *multi,
silent_error:
Py_XDECREF(result);
- PyEval_ReleaseThread(tmp_state);
+ PYCURL_RELEASE_THREAD();
return ret;
verbose_error:
PyErr_Print();
@@ -2634,14 +2711,10 @@ do_multi_socket_action(CurlMultiObject *self, PyObject *args)
if (check_multi_state(self, 1 | 2, "socket_action") != 0) {
return NULL;
}
- /* Release global lock and start */
- self->state = PyThreadState_Get();
- assert(self->state != NULL);
- Py_BEGIN_ALLOW_THREADS
+ PYCURL_BEGIN_ALLOW_THREADS
res = curl_multi_socket_action(self->multi_handle, socket, ev_bitmask, &running);
- Py_END_ALLOW_THREADS
- self->state = NULL;
+ PYCURL_END_ALLOW_THREADS
if (res != CURLM_OK) {
CURLERROR_MSG("multi_socket_action failed");
@@ -2662,13 +2735,9 @@ do_multi_socket_all(CurlMultiObject *self)
return NULL;
}
- /* Release global lock and start */
- self->state = PyThreadState_Get();
- assert(self->state != NULL);
- Py_BEGIN_ALLOW_THREADS
+ PYCURL_BEGIN_ALLOW_THREADS
res = curl_multi_socket_all(self->multi_handle, &running);
- Py_END_ALLOW_THREADS
- self->state = NULL;
+ PYCURL_END_ALLOW_THREADS
/* We assume these errors are ok, otherwise throw exception */
if (res != CURLM_OK && res != CURLM_CALL_MULTI_PERFORM) {
@@ -2692,13 +2761,9 @@ do_multi_perform(CurlMultiObject *self)
return NULL;
}
- /* Release global lock and start */
- self->state = PyThreadState_Get();
- assert(self->state != NULL);
- Py_BEGIN_ALLOW_THREADS
+ PYCURL_BEGIN_ALLOW_THREADS
res = curl_multi_perform(self->multi_handle, &running);
- Py_END_ALLOW_THREADS
- self->state = NULL;
+ PYCURL_END_ALLOW_THREADS
/* We assume these errors are ok, otherwise throw exception */
if (res != CURLM_OK && res != CURLM_CALL_MULTI_PERFORM) {
@@ -2722,16 +2787,20 @@ check_multi_add_remove(const CurlMultiObject *self, const CurlObject *obj)
PyErr_SetString(ErrorObject, "cannot add/remove handle - multi-stack is closed");
return -1;
}
+#ifdef WITH_THREAD
if (self->state != NULL) {
PyErr_SetString(ErrorObject, "cannot add/remove handle - multi_perform() already running");
return -1;
}
+#endif
/* check CurlObject status */
assert_curl_state(obj);
+#ifdef WITH_THREAD
if (obj->state != NULL) {
PyErr_SetString(ErrorObject, "cannot add/remove handle - perform() of curl object already running");
return -1;
}
+#endif
if (obj->multi_stack != NULL && obj->multi_stack != self) {
PyErr_SetString(ErrorObject, "cannot add/remove handle - curl object already on another multi-stack");
return -1;
@@ -3989,8 +4058,10 @@ initpycurl(void)
pycurl_ssl_init();
#endif
+#ifdef WITH_THREAD
/* Finally initialize global interpreter lock */
PyEval_InitThreads();
+#endif
}
--
1.7.1
From 9c06c6f29f4fcb5b6be9200f96c19b54b71e4b3b Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 12 Jun 2013 00:48:14 -0400
Subject: [PATCH 134/149] Test for seek function
Original patch by Jonas <seventies@users.sourceforge.net>.
http://sourceforge.net/p/pycurl/patches/8/
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
.gitignore | 1 +
Makefile | 1 +
tests/seek_function_test.py | 72 +++++++++++++++++++++++++++++++++++++++++++
tests/vsftpd.conf | 4 ++-
4 files changed, 77 insertions(+), 1 deletions(-)
create mode 100644 tests/seek_function_test.py
diff --git a/.gitignore b/.gitignore
index c873e32..abc741a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@
/MANIFEST
/build
/dist
+/tests/tmp
/www/htdocs/download/*.bz2
/www/htdocs/download/*.exe
/www/htdocs/download/*.gz
diff --git a/Makefile b/Makefile
index 9475250..cb12aaf 100644
--- a/Makefile
+++ b/Makefile
@@ -16,6 +16,7 @@ build-7.10.8:
$(PYTHON) setup.py build --curl-config=/home/hosts/localhost/packages/curl-7.10.8/bin/curl-config
test: build
+ mkdir -p tests/tmp
PYTHONPATH=$$(ls -d build/lib.*):$$PYTHONPATH \
$(NOSETESTS)
diff --git a/tests/seek_function_test.py b/tests/seek_function_test.py
new file mode 100644
index 0000000..2a28078
--- /dev/null
+++ b/tests/seek_function_test.py
@@ -0,0 +1,72 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+# Note: this test is meant to be run from pycurl project root.
+
+import pycurl
+import unittest
+import os.path
+
+from . import util
+from . import procmgr
+
+setup_module, teardown_module = procmgr.vsftpd_setup()
+
+class PartialFileSource:
+ def __init__(self):
+ self.__buf = '1234567890.1234567890'
+ self.__maxread = None
+ self.__bufptr = 0
+
+ def read(self, size):
+ p = self.__bufptr
+ end = p+size
+ if self.__maxread:
+ end = min(self.__maxread, end)
+ ret = self.__buf[p:end]
+ self.__bufptr+= len(ret)
+ #print 20*">>>", "read(%s) ==> %s" % (size, len(ret))
+ return ret
+
+ def seek(self, offset, origin):
+ #print 20*">>>", "seek(%s, %s)" % (offset, origin)
+ self.__bufptr = offset
+
+ def set_maxread(self, maxread):
+ self.__maxread = maxread
+
+class SeekFunctionTest(unittest.TestCase):
+ def test_seek_function(self):
+ c = pycurl.Curl()
+ c.setopt(pycurl.UPLOAD, 1)
+ c.setopt(pycurl.URL, "ftp://localhost:8321/tests/tmp/upload.txt")
+ c.setopt(pycurl.RESUME_FROM, 0)
+ #c.setopt(pycurl.VERBOSE, 1)
+ upload_file = PartialFileSource()
+ c.setopt(pycurl.READFUNCTION, upload_file.read)
+ upload_file.set_maxread(10)
+ c.perform()
+
+ with open(os.path.join(os.path.dirname(__file__), 'tmp', 'upload.txt')) as f:
+ content = f.read()
+ self.assertEqual('1234567890', content)
+
+ c.close()
+ del c
+ del upload_file
+
+ c = pycurl.Curl()
+ c.setopt(pycurl.URL, "ftp://localhost:8321/tests/tmp/upload.txt")
+ c.setopt(pycurl.RESUME_FROM, -1)
+ c.setopt(pycurl.UPLOAD, 1)
+ #c.setopt(pycurl.VERBOSE, 1)
+ upload_file = PartialFileSource()
+ c.setopt(pycurl.READFUNCTION, upload_file.read)
+ c.setopt(pycurl.SEEKFUNCTION, upload_file.seek)
+ c.perform()
+ c.close()
+
+ with open(os.path.join(os.path.dirname(__file__), 'tmp', 'upload.txt')) as f:
+ content = f.read()
+ self.assertEqual('1234567890.1234567890', content)
diff --git a/tests/vsftpd.conf b/tests/vsftpd.conf
index b4e4972..787da0e 100644
--- a/tests/vsftpd.conf
+++ b/tests/vsftpd.conf
@@ -5,7 +5,9 @@ background=no
download_enable=no
listen=yes
run_as_launching_user=yes
-write_enable=no
+write_enable=yes
+anon_upload_enable=yes
+anon_other_write_enable=yes
listen_port=8321
# should be supplied on command line
anon_root=/var/empty
--
1.7.1
From 7445cc96205e9c2b734ec07955519db224aa3ab7 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 12 Jun 2013 18:01:16 -0400
Subject: [PATCH 135/149] Allow multiple ftp tests to work in the suite
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/procmgr.py | 15 ++++++++++-----
1 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/tests/procmgr.py b/tests/procmgr.py
index ce08da9..18c3dd2 100644
--- a/tests/procmgr.py
+++ b/tests/procmgr.py
@@ -12,6 +12,7 @@ class ProcessManager(object):
def start(self):
self.process = subprocess.Popen(self.cmd)
+ self.running = True
self.thread = threading.Thread(target=self.run)
self.thread.daemon = True
@@ -19,11 +20,18 @@ class ProcessManager(object):
def run(self):
self.process.communicate()
+
+ def stop(self):
+ try:
+ os.kill(self.process.pid, signal.SIGTERM)
+ except OSError:
+ pass
+ self.running = False
managers = {}
def start(cmd):
- if str(cmd) in managers:
+ if str(cmd) in managers and managers[str(cmd)].running:
# already started
return
@@ -75,9 +83,6 @@ def vsftpd_setup():
except KeyError:
pass
else:
- try:
- os.kill(manager.process.pid, signal.SIGTERM)
- except OSError:
- pass
+ manager.stop()
return do_setup_module, teardown_module
--
1.7.1
From 4f8e4def8eddac6d5e5239602ef00230a2be3690 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Wed, 12 Jun 2013 19:26:25 -0400
Subject: [PATCH 136/149] Do not run vsftpd tests by default due to security implications
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
README.rst | 18 ++++++++++++++++++
tests/procmgr.py | 14 +++++++++++++-
2 files changed, 31 insertions(+), 1 deletions(-)
diff --git a/README.rst b/README.rst
index 3518d9d..c4427aa 100644
--- a/README.rst
+++ b/README.rst
@@ -49,6 +49,24 @@ or `pip`_::
.. _easy_install: http://peak.telecommunity.com/DevCenter/EasyInstall
.. _pip: http://pypi.python.org/pypi/pip
+Automated Tests
+---------------
+
+PycURL comes with an automated test suite. To run the tests, execute::
+
+ make test
+
+Some tests use vsftpd configured to accept anonymous uploads. These tests
+are not run by default. As configured, vsftpd will allow reads and writes to
+anything the user running the tests has read and write access. To run
+vsftpd tests you must explicitly set PYCURL_VSFTPD_PATH variable like so::
+
+ # use vsftpd in PATH
+ export PYCURL_VSFTPD_PATH=vsftpd
+
+ # specify full path to vsftpd
+ export PYCURL_VSFTPD_PATH=/usr/local/libexec/vsftpd
+
Contribute
----------
diff --git a/tests/procmgr.py b/tests/procmgr.py
index 18c3dd2..9309aa8 100644
--- a/tests/procmgr.py
+++ b/tests/procmgr.py
@@ -3,6 +3,7 @@ import subprocess
import os
import sys
import signal
+import nose.plugins.skip
from . import runwsgi
@@ -50,7 +51,16 @@ def start_setup(cmd):
if 'PYCURL_VSFTPD_PATH' in os.environ:
vsftpd_path = os.environ['PYCURL_VSFTPD_PATH']
else:
- vsftpd_path = 'vsftpd'
+ vsftpd_path = None
+
+try:
+ # python 2
+ exception_base = StandardError
+except NameError:
+ # python 3
+ exception_base = Exception
+class VsftpdNotConfigured(exception_base):
+ pass
def vsftpd_setup():
config_file_path = os.path.join(os.path.dirname(__file__), 'vsftpd.conf')
@@ -62,6 +72,8 @@ def vsftpd_setup():
]
setup_module = start_setup(cmd)
def do_setup_module():
+ if vsftpd_path is None:
+ raise nose.plugins.skip.SkipTest('PYCURL_VSFTPD_PATH environment variable not set')
try:
setup_module()
except OSError:
--
1.7.1
From 079b262507e77a61772255aea969471b2d81f16c Mon Sep 17 00:00:00 2001
From: Romulo A. Ceccon <romuloceccon@gmail.com>
Date: Tue, 16 Jul 2013 11:05:54 -0300
Subject: [PATCH 137/149] Adjusted declaration order for C89 compilers
PYCURL_DECLARE_THREAD_STATE looks like an
executable statement for some non-C99 compilers
(e.g. armcc), so it must appear at the end of
the declaration list.
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
src/pycurl.c | 19 ++++++++++---------
1 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/src/pycurl.c b/src/pycurl.c
index a9923ed..cdb934e 100644
--- a/src/pycurl.c
+++ b/src/pycurl.c
@@ -1140,12 +1140,12 @@ static size_t
util_write_callback(int flags, char *ptr, size_t size, size_t nmemb, void *stream)
{
CurlObject *self;
- PYCURL_DECLARE_THREAD_STATE;
PyObject *arglist;
PyObject *result = NULL;
size_t ret = 0; /* assume error */
PyObject *cb;
int total_size;
+ PYCURL_DECLARE_THREAD_STATE;
/* acquire thread */
self = (CurlObject *)stream;
@@ -1219,8 +1219,8 @@ opensocket_callback(void *clientp, curlsocktype purpose,
PyObject *result = NULL;
PyObject *fileno_result = NULL;
CurlObject *self;
- PYCURL_DECLARE_THREAD_STATE;
int ret = CURL_SOCKET_BAD;
+ PYCURL_DECLARE_THREAD_STATE;
self = (CurlObject *)clientp;
PYCURL_ACQUIRE_THREAD();
@@ -1269,12 +1269,12 @@ static int
seek_callback(void *stream, curl_off_t offset, int origin)
{
CurlObject *self;
- PYCURL_DECLARE_THREAD_STATE;
PyObject *arglist;
PyObject *result = NULL;
int ret = 2; /* assume error 2 (can't seek, libcurl free to work around). */
PyObject *cb;
int source = 0; /* assume beginning */
+ PYCURL_DECLARE_THREAD_STATE;
/* acquire thread */
self = (CurlObject *)stream;
@@ -1343,13 +1343,14 @@ static size_t
read_callback(char *ptr, size_t size, size_t nmemb, void *stream)
{
CurlObject *self;
- PYCURL_DECLARE_THREAD_STATE;
PyObject *arglist;
PyObject *result = NULL;
size_t ret = CURL_READFUNC_ABORT; /* assume error, this actually works */
int total_size;
+ PYCURL_DECLARE_THREAD_STATE;
+
/* acquire thread */
self = (CurlObject *)stream;
if (!PYCURL_ACQUIRE_THREAD())
@@ -1424,10 +1425,10 @@ progress_callback(void *stream,
double dltotal, double dlnow, double ultotal, double ulnow)
{
CurlObject *self;
- PYCURL_DECLARE_THREAD_STATE;
PyObject *arglist;
PyObject *result = NULL;
int ret = 1; /* assume error */
+ PYCURL_DECLARE_THREAD_STATE;
/* acquire thread */
self = (CurlObject *)stream;
@@ -1473,10 +1474,10 @@ debug_callback(CURL *curlobj, curl_infotype type,
char *buffer, size_t total_size, void *stream)
{
CurlObject *self;
- PYCURL_DECLARE_THREAD_STATE;
PyObject *arglist;
PyObject *result = NULL;
int ret = 0; /* always success */
+ PYCURL_DECLARE_THREAD_STATE;
UNUSED(curlobj);
@@ -1518,10 +1519,10 @@ static curlioerr
ioctl_callback(CURL *curlobj, int cmd, void *stream)
{
CurlObject *self;
- PYCURL_DECLARE_THREAD_STATE;
PyObject *arglist;
PyObject *result = NULL;
int ret = CURLIOE_FAILRESTART; /* assume error */
+ PYCURL_DECLARE_THREAD_STATE;
UNUSED(curlobj);
@@ -2492,10 +2493,10 @@ int multi_socket_callback(CURL *easy,
{
CurlMultiObject *self;
CurlObject *easy_self;
- PYCURL_DECLARE_THREAD_STATE;
PyObject *arglist;
PyObject *result = NULL;
int ret;
+ PYCURL_DECLARE_THREAD_STATE;
/* acquire thread */
self = (CurlMultiObject *)userp;
@@ -2539,10 +2540,10 @@ int multi_timer_callback(CURLM *multi,
void *userp)
{
CurlMultiObject *self;
- PYCURL_DECLARE_THREAD_STATE;
PyObject *arglist;
PyObject *result = NULL;
int ret = 0; /* always success */
+ PYCURL_DECLARE_THREAD_STATE;
UNUSED(multi);
--
1.7.1
From 350016b90e38505eeee46358d5303de2a7faca5e Mon Sep 17 00:00:00 2001
From: Romulo A. Ceccon <romuloceccon@gmail.com>
Date: Tue, 16 Jul 2013 11:13:52 -0300
Subject: [PATCH 138/149] Silence compiler hints about unused variables
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
src/pycurl.c | 4 +---
1 files changed, 1 insertions(+), 3 deletions(-)
diff --git a/src/pycurl.c b/src/pycurl.c
index cdb934e..7b96b01 100644
--- a/src/pycurl.c
+++ b/src/pycurl.c
@@ -851,7 +851,6 @@ static int
util_curl_init(CurlObject *self)
{
int res;
- char *s = NULL;
/* Set curl error buffer and zero it */
res = curl_easy_setopt(self->handle, CURLOPT_ERRORBUFFER, self->error);
@@ -2495,12 +2494,11 @@ int multi_socket_callback(CURL *easy,
CurlObject *easy_self;
PyObject *arglist;
PyObject *result = NULL;
- int ret;
PYCURL_DECLARE_THREAD_STATE;
/* acquire thread */
self = (CurlMultiObject *)userp;
- ret = curl_easy_getinfo(easy, CURLINFO_PRIVATE, &easy_self);
+ curl_easy_getinfo(easy, CURLINFO_PRIVATE, &easy_self);
if (!PYCURL_ACQUIRE_THREAD_MULTI())
return 0;
--
1.7.1
From 19af5bbbc98dc2198cc7541b81ed313a75faeb9c Mon Sep 17 00:00:00 2001
From: Romulo A. Ceccon <romuloceccon@gmail.com>
Date: Tue, 16 Jul 2013 11:44:36 -0300
Subject: [PATCH 139/149] Added support for CURLOPT_RESOLVE
http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTRESOLVE
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
src/pycurl.c | 36 ++++++++++++++++++++++++++++++++----
1 files changed, 32 insertions(+), 4 deletions(-)
diff --git a/src/pycurl.c b/src/pycurl.c
index 7b96b01..bb6056e 100644
--- a/src/pycurl.c
+++ b/src/pycurl.c
@@ -68,6 +68,10 @@
#define HAVE_CURLOPT_PROXYUSERNAME
#endif
+#if LIBCURL_VERSION_NUM >= 0x071503 /* check for 7.21.3 or greater */
+#define HAVE_CURLOPT_RESOLVE
+#endif
+
/* Python < 2.5 compat for Py_ssize_t */
#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
typedef int Py_ssize_t;
@@ -185,6 +189,9 @@ typedef struct {
struct curl_slist *quote;
struct curl_slist *postquote;
struct curl_slist *prequote;
+#ifdef HAVE_CURLOPT_RESOLVE
+ struct curl_slist *resolve;
+#endif
/* callbacks */
PyObject *w_cb;
PyObject *h_cb;
@@ -824,6 +831,9 @@ util_curl_new(void)
self->quote = NULL;
self->postquote = NULL;
self->prequote = NULL;
+#ifdef HAVE_CURLOPT_RESOLVE
+ self->resolve = NULL;
+#endif
/* Set callback pointers to NULL by default */
self->w_cb = NULL;
@@ -1025,6 +1035,9 @@ util_curl_close(CurlObject *self)
SFREE(self->quote);
SFREE(self->postquote);
SFREE(self->prequote);
+#ifdef HAVE_CURLOPT_RESOLVE
+ SFREE(self->resolve);
+#endif
#undef SFREE
}
@@ -1588,6 +1601,9 @@ do_curl_reset(CurlObject *self)
SFREE(self->quote);
SFREE(self->postquote);
SFREE(self->prequote);
+#ifdef HAVE_CURLOPT_RESOLVE
+ SFREE(self->resolve);
+#endif
#undef SFREE
res = util_curl_init(self);
if (res < 0) {
@@ -1933,6 +1949,11 @@ do_curl_setopt(CurlObject *self, PyObject *args)
case CURLOPT_PREQUOTE:
old_slist = &self->prequote;
break;
+#ifdef HAVE_CURLOPT_RESOLVE
+ case CURLOPT_RESOLVE:
+ old_slist = &self->resolve;
+ break;
+#endif
case CURLOPT_HTTPPOST:
break;
default:
@@ -1944,10 +1965,14 @@ do_curl_setopt(CurlObject *self, PyObject *args)
len = PyList_Size(obj);
if (len == 0) {
/* Empty list - do nothing */
- if (!(option == CURLOPT_HTTPHEADER ||
- option == CURLOPT_QUOTE ||
- option == CURLOPT_POSTQUOTE ||
- option == CURLOPT_PREQUOTE)) {
+ if (!(option == CURLOPT_HTTPHEADER
+ || option == CURLOPT_QUOTE
+ || option == CURLOPT_POSTQUOTE
+ || option == CURLOPT_PREQUOTE
+#ifdef HAVE_CURLOPT_RESOLVE
+ || option == CURLOPT_RESOLVE
+#endif
+ )) {
/* Empty list - do nothing */
Py_INCREF(Py_None);
return Py_None;
@@ -3888,6 +3913,9 @@ initpycurl(void)
insint_c(d, "CRLFILE", CURLOPT_CRLFILE);
insint_c(d, "ISSUERCERT", CURLOPT_ISSUERCERT);
insint_c(d, "ADDRESS_SCOPE", CURLOPT_ADDRESS_SCOPE);
+#ifdef HAVE_CURLOPT_RESOLVE
+ insint_c(d, "RESOLVE", CURLOPT_RESOLVE);
+#endif
insint_c(d, "M_TIMERFUNCTION", CURLMOPT_TIMERFUNCTION);
insint_c(d, "M_SOCKETFUNCTION", CURLMOPT_SOCKETFUNCTION);
--
1.7.1
From 576efa57797d878e23dd97b1f5ec2393167b30c6 Mon Sep 17 00:00:00 2001
From: Romulo A. Ceccon <romuloceccon@gmail.com>
Date: Fri, 19 Jul 2013 11:36:43 -0300
Subject: [PATCH 140/149] Added test-case for CURLOPT_RESOLVE
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/resolve_test.py | 22 ++++++++++++++++++++++
1 files changed, 22 insertions(+), 0 deletions(-)
create mode 100644 tests/resolve_test.py
diff --git a/tests/resolve_test.py b/tests/resolve_test.py
new file mode 100644
index 0000000..fcee830
--- /dev/null
+++ b/tests/resolve_test.py
@@ -0,0 +1,22 @@
+# -*- coding: iso-8859-1 -*-
+
+import pycurl
+import unittest
+
+from . import app
+from . import runwsgi
+
+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8380))
+
+class ResolveTest(unittest.TestCase):
+ def setUp(self):
+ self.curl = pycurl.Curl()
+
+ def tearDown(self):
+ self.curl.close()
+
+ def test_resolve(self):
+ self.curl.setopt(pycurl.URL, 'http://p.localhost:8380/success')
+ self.curl.setopt(pycurl.RESOLVE, ['p.localhost:8380:127.0.0.1'])
+ self.curl.perform()
+ self.assertEqual(200, self.curl.getinfo(pycurl.RESPONSE_CODE))
--
1.7.1
From 2ebc6b6097df0a83a6982e8941af3e880e18a74f Mon Sep 17 00:00:00 2001
From: Romulo A. Ceccon <romuloceccon@gmail.com>
Date: Fri, 19 Jul 2013 11:24:40 -0300
Subject: [PATCH 141/149] Test suite documentation
Briefly clarified documentation about dependencies
of the test suite.
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
README.rst | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/README.rst b/README.rst
index c4427aa..b416b97 100644
--- a/README.rst
+++ b/README.rst
@@ -56,6 +56,8 @@ PycURL comes with an automated test suite. To run the tests, execute::
make test
+The suite depends on packages `nose`_ and `bottle`_.
+
Some tests use vsftpd configured to accept anonymous uploads. These tests
are not run by default. As configured, vsftpd will allow reads and writes to
anything the user running the tests has read and write access. To run
@@ -67,6 +69,9 @@ vsftpd tests you must explicitly set PYCURL_VSFTPD_PATH variable like so::
# specify full path to vsftpd
export PYCURL_VSFTPD_PATH=/usr/local/libexec/vsftpd
+.. _nose: https://nose.readthedocs.org/
+.. _bottle: http://bottlepy.org/
+
Contribute
----------
--
1.7.1
From 59fd64c38e4adf28242dca3fd3d3a0167016945a Mon Sep 17 00:00:00 2001
From: Wim Lewis <wiml@users.sourceforge.net>
Date: Thu, 21 Jul 2011 12:00:00 -0400
Subject: [PATCH 142/149] Add suport for CURLINFO_CERTINFO and CURLOPT_CERTINFO.
This patch adds support for CURLINFO_CERTINFO (and CURLOPT_CERTINFO),
as well as documentation for them and a couple of minor documentation fixes.
These options appeared in libcurl 7.19.1.
The format of the returned information was chosen to be reasonably close
to the underlying libcurl data structure, while also allowing a
Python programmer to pass the cerificate info to dict() in order to get
a more convenient representation.
https://sourceforge.net/p/pycurl/patches/14/
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
doc/curlobject.html | 19 ++++++++++-
src/pycurl.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 100 insertions(+), 2 deletions(-)
diff --git a/doc/curlobject.html b/doc/curlobject.html
index a3d421f..394b950 100644
--- a/doc/curlobject.html
+++ b/doc/curlobject.html
@@ -43,9 +43,11 @@ href="http://curl.haxx.se/libcurl/c/curl_easy_reset.html"><code>curl_easy_reset<
<p>Corresponds to
<a href="http://curl.haxx.se/libcurl/c/curl_easy_setopt.html"><code>curl_easy_setopt</code></a> in libcurl, where
<em>option</em> is specified with the CURLOPT_* constants in libcurl,
-except that the CURLOPT_ prefix has been removed. The type for
+except that the CURLOPT_ prefix has been removed.
+(See below for exceptions.)
+The type for
<em>value</em> depends on the option, and can be either a string,
-integer, long integer, file objects, lists, or functions.</p>
+integer, long integer, file object, list, or function.</p>
<p>Example usage:</p>
@@ -72,6 +74,7 @@ print b.getvalue()
<a href="http://curl.haxx.se/libcurl/c/curl_easy_getinfo.html"><code>curl_easy_getinfo</code></a> in libcurl, where
<em>option</em> is the same as the CURLINFO_* constants in libcurl,
except that the CURLINFO_ prefix has been removed.
+(See below for exceptions.)
<em>Result</em> contains an integer, float or string, depending on
which option is given. The <code>getinfo</code> method should
not be called unless <code>perform</code> has been called and
@@ -97,6 +100,18 @@ print c.getinfo(pycurl.HTTP_CODE), c.getinfo(pycurl.EFFECTIVE_URL)
</dd>
</dl>
+<p>In order to distinguish between similarly-named CURLOPT and
+CURLINFO constants, some have <code>OPT_</code>
+and <code>INFO_</code> prefixes. These are
+<code>INFO_FILETIME</code>, <code>OPT_FILETIME</code>,
+<code>INFO_COOKIELIST</code> (but <code>setopt</code> uses <code>COOKIELIST</code>!),
+<code>INFO_CERTINFO</code>, and <code>OPT_CERTINFO</code>.</p>
+
+<p>The value returned by <code>getinfo(INFO_CERTINFO)</code> is a list
+with one element per certificate in the chain, starting with the leaf;
+each element is a sequence
+of <code>(</code><em>key</em><code>, </code><em>value</em><code>)</code>
+tuples.</p>
<hr />
<p>
diff --git a/src/pycurl.c b/src/pycurl.c
index bb6056e..5da354d 100644
--- a/src/pycurl.c
+++ b/src/pycurl.c
@@ -297,6 +297,71 @@ error:
return NULL;
}
+/* Convert a struct curl_certinfo into a Python data structure.
+ * In case of error return NULL with an exception set.
+ */
+static PyObject *convert_certinfo(struct curl_certinfo *cinfo)
+{
+ PyObject *certs;
+ int cert_index;
+
+ if (!cinfo) {
+ certs = Py_None;
+ Py_INCREF(certs);
+ return certs;
+ }
+
+ certs = PyList_New((Py_ssize_t)(cinfo->num_of_certs));
+ if (!certs)
+ return NULL;
+
+ for (cert_index = 0; cert_index < cinfo->num_of_certs; cert_index ++) {
+ struct curl_slist *fields = cinfo->certinfo[cert_index];
+ struct curl_slist *field_cursor;
+ int field_count, field_index;
+ PyObject *cert;
+
+ field_count = 0;
+ field_cursor = fields;
+ while (field_cursor != NULL) {
+ field_cursor = field_cursor->next;
+ field_count ++;
+ }
+
+
+ cert = PyTuple_New((Py_ssize_t)field_count);
+ if (!cert)
+ goto error;
+ PyList_SetItem(certs, cert_index, cert); /* Eats the ref from New() */
+
+ for(field_index = 0, field_cursor = fields;
+ field_cursor != NULL;
+ field_index ++, field_cursor = field_cursor->next) {
+ const char *field = field_cursor->data;
+ PyObject *field_tuple;
+
+ if (!field) {
+ field_tuple = Py_None; Py_INCREF(field_tuple);
+ } else {
+ const char *sep = strchr(field, ':');
+ if (!sep) {
+ field_tuple = PyString_FromString(field);
+ } else {
+ field_tuple = Py_BuildValue("s#s", field, (int)(sep - field), sep+1);
+ }
+ if (!field_tuple)
+ goto error;
+ }
+ PyTuple_SET_ITEM(cert, field_index, field_tuple); /* Eats the ref */
+ }
+ }
+
+ return certs;
+
+ error:
+ Py_XDECREF(certs);
+ return NULL;
+}
#ifdef WITH_THREAD
/*************************************************************************
@@ -1673,6 +1738,10 @@ util_curl_unsetopt(CurlObject *self, int option)
SETOPT((char *) 0);
break;
+ case CURLOPT_CERTINFO:
+ SETOPT((long) 0);
+ break;
+
/* info: we explicitly list unsupported options here */
case CURLOPT_COOKIEFILE:
default:
@@ -2394,6 +2463,18 @@ do_curl_getinfo(CurlObject *self, PyObject *args)
}
return convert_slist(slist, 1 | 2);
}
+
+ case CURLINFO_CERTINFO:
+ {
+ /* Return a list of lists of 2-tuples */
+ struct curl_certinfo *clist = NULL;
+ res = curl_easy_getinfo(self->handle, CURLINFO_CERTINFO, &clist);
+ if (res != CURLE_OK) {
+ CURLERROR_RETVAL();
+ } else {
+ return convert_certinfo(clist);
+ }
+ }
}
/* Got wrong option on the method call */
@@ -3916,6 +3997,7 @@ initpycurl(void)
#ifdef HAVE_CURLOPT_RESOLVE
insint_c(d, "RESOLVE", CURLOPT_RESOLVE);
#endif
+ insint_c(d, "OPT_CERTINFO", CURLOPT_CERTINFO);
insint_c(d, "M_TIMERFUNCTION", CURLMOPT_TIMERFUNCTION);
insint_c(d, "M_SOCKETFUNCTION", CURLMOPT_SOCKETFUNCTION);
@@ -3993,6 +4075,7 @@ initpycurl(void)
insint_c(d, "INFO_COOKIELIST", CURLINFO_COOKIELIST);
insint_c(d, "LASTSOCKET", CURLINFO_LASTSOCKET);
insint_c(d, "FTP_ENTRY_PATH", CURLINFO_FTP_ENTRY_PATH);
+ insint_c(d, "INFO_CERTINFO", CURLINFO_CERTINFO);
/* options for global_init() */
insint(d, "GLOBAL_SSL", CURL_GLOBAL_SSL);
--
1.7.1
From aa82321393ebc813d326cb305a1e2f73bbe45abe Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Thu, 30 May 2013 14:42:38 -0400
Subject: [PATCH 143/149] Conditional compilation for CERTINFO bits
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
src/pycurl.c | 11 +++++++++++
1 files changed, 11 insertions(+), 0 deletions(-)
diff --git a/src/pycurl.c b/src/pycurl.c
index 5da354d..c0fec4b 100644
--- a/src/pycurl.c
+++ b/src/pycurl.c
@@ -66,6 +66,7 @@
LIBCURL_VERSION_MAJOR == 7 && LIBCURL_VERSION_MINOR == 19 && LIBCURL_VERSION_PATCH >= 1
#define HAVE_CURLOPT_USERNAME
#define HAVE_CURLOPT_PROXYUSERNAME
+#define HAVE_CURLOPT_CERTINFO
#endif
#if LIBCURL_VERSION_NUM >= 0x071503 /* check for 7.21.3 or greater */
@@ -297,6 +298,7 @@ error:
return NULL;
}
+#ifdef HAVE_CURLOPT_CERTINFO
/* Convert a struct curl_certinfo into a Python data structure.
* In case of error return NULL with an exception set.
*/
@@ -362,6 +364,7 @@ static PyObject *convert_certinfo(struct curl_certinfo *cinfo)
Py_XDECREF(certs);
return NULL;
}
+#endif
#ifdef WITH_THREAD
/*************************************************************************
@@ -1738,9 +1741,11 @@ util_curl_unsetopt(CurlObject *self, int option)
SETOPT((char *) 0);
break;
+#ifdef HAVE_CURLOPT_CERTINFO
case CURLOPT_CERTINFO:
SETOPT((long) 0);
break;
+#endif
/* info: we explicitly list unsupported options here */
case CURLOPT_COOKIEFILE:
@@ -2464,6 +2469,7 @@ do_curl_getinfo(CurlObject *self, PyObject *args)
return convert_slist(slist, 1 | 2);
}
+#ifdef HAVE_CURLOPT_CERTINFO
case CURLINFO_CERTINFO:
{
/* Return a list of lists of 2-tuples */
@@ -2476,6 +2482,7 @@ do_curl_getinfo(CurlObject *self, PyObject *args)
}
}
}
+#endif
/* Got wrong option on the method call */
PyErr_SetString(PyExc_ValueError, "invalid argument to getinfo");
@@ -3997,7 +4004,9 @@ initpycurl(void)
#ifdef HAVE_CURLOPT_RESOLVE
insint_c(d, "RESOLVE", CURLOPT_RESOLVE);
#endif
+#ifdef HAVE_CURLOPT_CERTINFO
insint_c(d, "OPT_CERTINFO", CURLOPT_CERTINFO);
+#endif
insint_c(d, "M_TIMERFUNCTION", CURLMOPT_TIMERFUNCTION);
insint_c(d, "M_SOCKETFUNCTION", CURLMOPT_SOCKETFUNCTION);
@@ -4075,7 +4084,9 @@ initpycurl(void)
insint_c(d, "INFO_COOKIELIST", CURLINFO_COOKIELIST);
insint_c(d, "LASTSOCKET", CURLINFO_LASTSOCKET);
insint_c(d, "FTP_ENTRY_PATH", CURLINFO_FTP_ENTRY_PATH);
+#ifdef HAVE_CURLOPT_CERTINFO
insint_c(d, "INFO_CERTINFO", CURLINFO_CERTINFO);
+#endif
/* options for global_init() */
insint(d, "GLOBAL_SSL", CURL_GLOBAL_SSL);
--
1.7.1
From 68713450eef4fd4252babafefe24d42faebc6de9 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Thu, 30 May 2013 14:49:44 -0400
Subject: [PATCH 144/149] Use an ordinary DECREF
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
src/pycurl.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/src/pycurl.c b/src/pycurl.c
index c0fec4b..c8b8402 100644
--- a/src/pycurl.c
+++ b/src/pycurl.c
@@ -361,7 +361,7 @@ static PyObject *convert_certinfo(struct curl_certinfo *cinfo)
return certs;
error:
- Py_XDECREF(certs);
+ Py_DECREF(certs);
return NULL;
}
#endif
--
1.7.1
From 53999f0ca88f6a71ff0b9331ce9da89a5f07fb0f Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Thu, 30 May 2013 15:35:31 -0400
Subject: [PATCH 145/149] SSL certinfo test using github
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/certinfo_test.py | 52 ++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 52 insertions(+), 0 deletions(-)
create mode 100644 tests/certinfo_test.py
diff --git a/tests/certinfo_test.py b/tests/certinfo_test.py
new file mode 100644
index 0000000..25c05af
--- /dev/null
+++ b/tests/certinfo_test.py
@@ -0,0 +1,52 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+
+import pycurl
+import unittest
+import nose.plugins.skip
+
+from . import util
+
+class CertinfoTest(unittest.TestCase):
+ def setUp(self):
+ self.curl = pycurl.Curl()
+
+ def tearDown(self):
+ self.curl.close()
+
+ def test_certinfo_option(self):
+ # CURLOPT_CERTINFO was introduced in libcurl-7.19.1
+ if util.pycurl_version_less_than(7, 19, 1):
+ raise nose.plugins.skip.SkipTest('libcurl < 7.19.1')
+
+ assert hasattr(pycurl, 'OPT_CERTINFO')
+
+ def test_request_without_certinfo(self):
+ # CURLOPT_CERTINFO was introduced in libcurl-7.19.1
+ if util.pycurl_version_less_than(7, 19, 1):
+ raise nose.plugins.skip.SkipTest('libcurl < 7.19.1')
+
+ self.curl.setopt(pycurl.URL, 'https://github.com/')
+ sio = util.StringIO()
+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
+ self.curl.perform()
+ assert 'GitHub' in sio.getvalue()
+
+ certinfo = self.curl.getinfo(pycurl.INFO_CERTINFO)
+ self.assertEqual([], certinfo)
+
+ def test_request_with_certinfo(self):
+ # CURLOPT_CERTINFO was introduced in libcurl-7.19.1
+ if util.pycurl_version_less_than(7, 19, 1):
+ raise nose.plugins.skip.SkipTest('libcurl < 7.19.1')
+
+ self.curl.setopt(pycurl.URL, 'https://github.com/')
+ sio = util.StringIO()
+ self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
+ self.curl.setopt(pycurl.OPT_CERTINFO, 1)
+ self.curl.perform()
+ assert 'GitHub' in sio.getvalue()
+
+ certinfo = self.curl.getinfo(pycurl.INFO_CERTINFO)
+ assert len(certinfo) > 0
--
1.7.1
From 6ca51826965392ed3ac2338b5da7dac5215bd840 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Tue, 23 Jul 2013 20:10:47 -0400
Subject: [PATCH 146/149] Self signed certificate for ssl tests
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/certs/server.crt | 14 ++++++++++++++
tests/certs/server.key | 15 +++++++++++++++
2 files changed, 29 insertions(+), 0 deletions(-)
create mode 100644 tests/certs/server.crt
create mode 100644 tests/certs/server.key
diff --git a/tests/certs/server.crt b/tests/certs/server.crt
new file mode 100644
index 0000000..4a8decc
--- /dev/null
+++ b/tests/certs/server.crt
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICJTCCAY4CCQDfQAHGuFkN2zANBgkqhkiG9w0BAQUFADBXMQswCQYDVQQGEwJB
+VTETMBEGA1UECBMKU29tZS1TdGF0ZTEaMBgGA1UEChMRcHljdXJsIHRlc3Qgc3Vp
+dGUxFzAVBgNVBAMTDmxvY2FsaG9zdDo4MzgzMB4XDTEzMDcyNDAwMDgxNVoXDTE0
+MDcyNDAwMDgxNVowVzELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUx
+GjAYBgNVBAoTEXB5Y3VybCB0ZXN0IHN1aXRlMRcwFQYDVQQDEw5sb2NhbGhvc3Q6
+ODM4MzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAxE0+59Kf2z9ccZyUAuKG
+QpkQaXtEJC13exY4SWIfr78FfCStdqpZdfmm66djFENhmaAZYGsPHGXrEIHQqja2
+7KYkHo4cXLxksR4Db01yPMtMU9xHzg37OTIS2lGRmMxLduKc5XKsxA98PV/D1k4k
+sqLcLDH//YdLR0iYUYLOIgMCAwEAATANBgkqhkiG9w0BAQUFAAOBgQAppFdMNMHe
+68uQA1y2xAYW7faUH8/g+XAuH9WjLL2QfWGXgWey/pwofsrTO2Hl+D9y8Rey4eJ/
+BDv3OV2cBWBYBOxZv/kqyDHQc38tho9gdaPQnD4ttFk2TSgaOs1W39pGY1On0Ejd
+O6CXEGV7p8C613AgEkbdudnn+ChvyH/Shw==
+-----END CERTIFICATE-----
diff --git a/tests/certs/server.key b/tests/certs/server.key
new file mode 100644
index 0000000..5bdbbf9
--- /dev/null
+++ b/tests/certs/server.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDETT7n0p/bP1xxnJQC4oZCmRBpe0QkLXd7FjhJYh+vvwV8JK12
+qll1+abrp2MUQ2GZoBlgaw8cZesQgdCqNrbspiQejhxcvGSxHgNvTXI8y0xT3EfO
+Dfs5MhLaUZGYzEt24pzlcqzED3w9X8PWTiSyotwsMf/9h0tHSJhRgs4iAwIDAQAB
+AoGAMLNFTvgnJpqikaEZ61lNm8ojkze8oQkSolvR3QrV96D5eGIVEuKSTT2Blucx
+In7RAO8CPLRyzEXQuoiqPwBSAxY2Xswd+zcAgN2Zi8uqWTmPNsW6451BJRemgLjK
+OxLxCdVTOTxHfttj/CnwYQ6zn55oyZJGGmaVGykbvH/AgukCQQD3HfhOPExsI/6X
+Bp7CeZOhmM+LBOQGQGDjRnBdRp0s3HiUfaDxU2mbEafGPI2OyuzpYAqxHVTJLai6
+CQlJGuQXAkEAy1upObz2bcN2dUCHNufk2qdfRSCRkmKemuqznwCW3fSoRKB+qOu3
+xyTLEkTvLBNnAFjoyd6B75QzL/7//qvo9QJAE0xV3dY7qZ5N/YFY2Jsh+layroqd
+PBe++UDA+afQEnbNO9trvCzlbGS+k26bJ3GVeswzSY2e128nZA/cl8bv1QJAfTEO
+uybjpqtAj+qL03drYljLw+jK9Y2VCtYWgnqAZmAp/yW3FBMZbpXuFm8ttrqzHHmf
+xjcfUvivkoqv2n7GyQJBAKxbBVx/LQiSVpOTnXTEA1NJF8NS2NCF+3sm3kGhFKql
+Hi/cCAFrhBl9MoPJF/6noukfIkq0SzjkWrYIcoBDoVg=
+-----END RSA PRIVATE KEY-----
--
1.7.1
From 2bc1535519faf3e574bac514b634d29ed7e28c20 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Sat, 1 Jun 2013 05:23:41 -0400
Subject: [PATCH 147/149] Support for testing against a local ssl server
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/runwsgi.py | 34 +++++++++++++++++++++++++++++-----
1 files changed, 29 insertions(+), 5 deletions(-)
diff --git a/tests/runwsgi.py b/tests/runwsgi.py
index 114ce42..c9bc236 100644
--- a/tests/runwsgi.py
+++ b/tests/runwsgi.py
@@ -4,6 +4,7 @@ import sys
import bottle
import threading
import socket
+import os.path
import time as _time
try:
@@ -37,6 +38,21 @@ class Server(bottle.WSGIRefServer):
else:
self.srv.serve_forever(poll_interval=0.1)
+class SslServer(bottle.CherryPyServer):
+ def run(self, handler):
+ import cherrypy.wsgiserver, cherrypy.wsgiserver.ssl_builtin
+ server = cherrypy.wsgiserver.CherryPyWSGIServer((self.host, self.port), handler)
+ cert_dir = os.path.join(os.path.dirname(__file__), 'certs')
+ ssl_adapter = cherrypy.wsgiserver.ssl_builtin.BuiltinSSLAdapter(
+ os.path.join(cert_dir, 'server.crt'),
+ os.path.join(cert_dir, 'server.key'),
+ )
+ server.ssl_adapter = ssl_adapter
+ try:
+ server.start()
+ finally:
+ server.stop()
+
def wait_for_network_service(netloc, check_interval, num_attempts):
ok = False
for i in range(num_attempts):
@@ -51,8 +67,8 @@ def wait_for_network_service(netloc, check_interval, num_attempts):
break
return ok
-def start_bottle_server(app, port, **kwargs):
- server_thread = ServerThread(app, port, kwargs)
+def start_bottle_server(app, port, server, **kwargs):
+ server_thread = ServerThread(app, port, server, kwargs)
server_thread.daemon = True
server_thread.start()
@@ -64,12 +80,12 @@ def start_bottle_server(app, port, **kwargs):
return server_thread.server
class ServerThread(threading.Thread):
- def __init__(self, app, port, server_kwargs):
+ def __init__(self, app, port, server, server_kwargs):
threading.Thread.__init__(self)
self.app = app
self.port = port
self.server_kwargs = server_kwargs
- self.server = Server(host='localhost', port=self.port, **self.server_kwargs)
+ self.server = server(host='localhost', port=self.port, **self.server_kwargs)
def run(self):
bottle.run(self.app, server=self.server, quiet=True)
@@ -104,7 +120,15 @@ def app_runner_setup(*specs):
if port in started_servers:
assert started_servers[port] == (app, kwargs)
else:
- self.servers.append(start_bottle_server(app, port, **kwargs))
+ server = Server
+ if 'server' in kwargs:
+ server = kwargs['server']
+ del kwargs['server']
+ elif 'ssl' in kwargs:
+ if kwargs['ssl']:
+ server = SslServer
+ del kwargs['ssl']
+ self.servers.append(start_bottle_server(app, port, server, **kwargs))
started_servers[port] = (app, kwargs)
def teardown(self):
--
1.7.1
From 1bae541fbeb5d7554c29d76f7e57dc3bc6cb4985 Mon Sep 17 00:00:00 2001
From: Oleg Pudeyev <oleg@bsdpower.com>
Date: Tue, 23 Jul 2013 20:11:25 -0400
Subject: [PATCH 148/149] Test against a local ssl server
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
tests/certinfo_test.py | 26 +++++++++++++++++++++-----
1 files changed, 21 insertions(+), 5 deletions(-)
diff --git a/tests/certinfo_test.py b/tests/certinfo_test.py
index 25c05af..1473d39 100644
--- a/tests/certinfo_test.py
+++ b/tests/certinfo_test.py
@@ -6,8 +6,12 @@ import pycurl
import unittest
import nose.plugins.skip
+from . import app
+from . import runwsgi
from . import util
+setup_module, teardown_module = runwsgi.app_runner_setup((app.app, 8383, dict(ssl=True)))
+
class CertinfoTest(unittest.TestCase):
def setUp(self):
self.curl = pycurl.Curl()
@@ -27,11 +31,13 @@ class CertinfoTest(unittest.TestCase):
if util.pycurl_version_less_than(7, 19, 1):
raise nose.plugins.skip.SkipTest('libcurl < 7.19.1')
- self.curl.setopt(pycurl.URL, 'https://github.com/')
+ self.curl.setopt(pycurl.URL, 'https://localhost:8383/success')
sio = util.StringIO()
self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
+ # self signed certificate
+ self.curl.setopt(pycurl.SSL_VERIFYPEER, 0)
self.curl.perform()
- assert 'GitHub' in sio.getvalue()
+ assert sio.getvalue() == 'success'
certinfo = self.curl.getinfo(pycurl.INFO_CERTINFO)
self.assertEqual([], certinfo)
@@ -41,12 +47,22 @@ class CertinfoTest(unittest.TestCase):
if util.pycurl_version_less_than(7, 19, 1):
raise nose.plugins.skip.SkipTest('libcurl < 7.19.1')
- self.curl.setopt(pycurl.URL, 'https://github.com/')
+ self.curl.setopt(pycurl.URL, 'https://localhost:8383/success')
sio = util.StringIO()
self.curl.setopt(pycurl.WRITEFUNCTION, sio.write)
self.curl.setopt(pycurl.OPT_CERTINFO, 1)
+ # self signed certificate
+ self.curl.setopt(pycurl.SSL_VERIFYPEER, 0)
self.curl.perform()
- assert 'GitHub' in sio.getvalue()
+ assert sio.getvalue() == 'success'
certinfo = self.curl.getinfo(pycurl.INFO_CERTINFO)
- assert len(certinfo) > 0
+ # self signed certificate, one certificate in chain
+ assert len(certinfo) == 1
+ certinfo = certinfo[0]
+ # convert to a dictionary
+ certinfo_dict = {}
+ for entry in certinfo:
+ certinfo_dict[entry[0]] = entry[1]
+ assert 'Subject' in certinfo_dict
+ assert 'pycurl test suite' in certinfo_dict['Subject']
--
1.7.1
From bb2b2e6f5a7dd7a343e2c1207ca152dbeb4cf14b Mon Sep 17 00:00:00 2001
From: Romulo A. Ceccon <romuloceccon@gmail.com>
Date: Fri, 26 Jul 2013 10:23:26 -0300
Subject: [PATCH 149/149] Added documentation for dependency on CherryPy
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
---
README.rst | 3 ++-
requirements-dev.txt | 1 +
2 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/README.rst b/README.rst
index b416b97..e0641d0 100644
--- a/README.rst
+++ b/README.rst
@@ -56,7 +56,7 @@ PycURL comes with an automated test suite. To run the tests, execute::
make test
-The suite depends on packages `nose`_ and `bottle`_.
+The suite depends on packages `nose`_, `bottle`_ and `cherrypy`_.
Some tests use vsftpd configured to accept anonymous uploads. These tests
are not run by default. As configured, vsftpd will allow reads and writes to
@@ -71,6 +71,7 @@ vsftpd tests you must explicitly set PYCURL_VSFTPD_PATH variable like so::
.. _nose: https://nose.readthedocs.org/
.. _bottle: http://bottlepy.org/
+.. _cherrypy: http://www.cherrypy.org/
Contribute
----------
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 36b0b24..ea11ac9 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,2 +1,3 @@
bottle
nose
+cherrypy
--
1.7.1