Skip to content

Commit

Permalink
dns: add support for synchronous getaddrinfo
Browse files Browse the repository at this point in the history
  • Loading branch information
saghul committed Apr 9, 2015
1 parent d6eb307 commit 4c7f2c5
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 36 deletions.
94 changes: 62 additions & 32 deletions src/dns.c
Original file line number Diff line number Diff line change
@@ -1,31 +1,17 @@

static void
pyuv__getaddrinfo_cb(uv_getaddrinfo_t* req, int status, struct addrinfo* res)
static int
pyuv__getaddrinfo_process_result(int status, struct addrinfo* res, PyObject** dns_result)
{
PyGILState_STATE gstate = PyGILState_Ensure();
struct addrinfo *ptr;
Loop *loop;
GAIRequest *gai_req;
PyObject *addr, *item, *errorno, *dns_result, *result;

ASSERT(req);

gai_req = PYUV_CONTAINER_OF(req, GAIRequest, req);
loop = REQUEST(gai_req)->loop;
PyObject *addr, *item;

if (status != 0) {
errorno = PyInt_FromLong((long)status);
dns_result = Py_None;
Py_INCREF(Py_None);
goto callback;
return status;
}

dns_result = PyList_New(0);
if (!dns_result) {
errorno = PyInt_FromLong((long)UV_ENOMEM);
dns_result = Py_None;
Py_INCREF(Py_None);
goto callback;
*dns_result = PyList_New(0);
if (!*dns_result) {
return UV_ENOMEM;
}

for (ptr = res; ptr; ptr = ptr->ai_next) {
Expand All @@ -49,13 +35,38 @@ pyuv__getaddrinfo_cb(uv_getaddrinfo_t* req, int status, struct addrinfo* res)
PyStructSequence_SET_ITEM(item, 3, Py_BuildValue("s", ptr->ai_canonname ? ptr->ai_canonname : ""));
PyStructSequence_SET_ITEM(item, 4, addr);

PyList_Append(dns_result, item);
PyList_Append(*dns_result, item);
Py_DECREF(item);
}
errorno = Py_None;
Py_INCREF(Py_None);

callback:
return 0;
}


static void
pyuv__getaddrinfo_cb(uv_getaddrinfo_t* req, int status, struct addrinfo* res)
{
PyGILState_STATE gstate = PyGILState_Ensure();
Loop *loop;
GAIRequest *gai_req;
PyObject *errorno, *dns_result, *result;
int r;

ASSERT(req);

gai_req = PYUV_CONTAINER_OF(req, GAIRequest, req);
loop = REQUEST(gai_req)->loop;
dns_result = NULL;
errorno = NULL;

r = pyuv__getaddrinfo_process_result(status, res, &dns_result);
if (r == 0) {
PYUV_SET_NONE(errorno);
} else {
errorno = PyInt_FromLong((long)r);
PYUV_SET_NONE(dns_result);
}

result = PyObject_CallFunctionObjArgs(gai_req->callback, dns_result, errorno, NULL);
if (result == NULL) {
handle_uncaught_exception(loop);
Expand Down Expand Up @@ -131,16 +142,17 @@ Util_func_getaddrinfo(PyObject *obj, PyObject *args, PyObject *kwargs)
GAIRequest *gai_req;
PyObject *callback, *host, *service, *idna, *ascii;

static char *kwlist[] = {"loop", "callback", "host", "port", "family", "socktype", "protocol", "flags", NULL};
static char *kwlist[] = {"loop", "host", "port", "family", "socktype", "protocol", "flags", "callback", NULL};

UNUSED_ARG(obj);
gai_req = NULL;
idna = ascii = NULL;
port = socktype = protocol = flags = 0;
family = AF_UNSPEC;
service = Py_None;
callback = Py_None;

if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!OO|Oiiii:getaddrinfo", kwlist, &LoopType, &loop, &callback, &host, &service, &family, &socktype, &protocol, &flags)) {
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!O|OiiiiO:getaddrinfo", kwlist, &LoopType, &loop, &host, &service, &family, &socktype, &protocol, &flags, &callback)) {
return NULL;
}

Expand All @@ -158,8 +170,8 @@ Util_func_getaddrinfo(PyObject *obj, PyObject *args, PyObject *kwargs)
goto error;
}

if (!PyCallable_Check(callback)) {
PyErr_SetString(PyExc_TypeError, "a callable is required");
if (callback != Py_None && !PyCallable_Check(callback)) {
PyErr_SetString(PyExc_TypeError, "'callback' must be a callable or None");
goto error;
}

Expand Down Expand Up @@ -197,7 +209,12 @@ Util_func_getaddrinfo(PyObject *obj, PyObject *args, PyObject *kwargs)
hints.ai_protocol = protocol;
hints.ai_flags = flags;

err = uv_getaddrinfo(loop->uv_loop, &gai_req->req, &pyuv__getaddrinfo_cb, host_str, service_str, &hints);
err = uv_getaddrinfo(loop->uv_loop,
&gai_req->req,
callback != Py_None ? &pyuv__getaddrinfo_cb : NULL,
host_str,
service_str,
&hints);
if (err < 0) {
RAISE_UV_EXCEPTION(err, PyExc_UVError);
goto error;
Expand All @@ -206,8 +223,21 @@ Util_func_getaddrinfo(PyObject *obj, PyObject *args, PyObject *kwargs)
Py_XDECREF(idna);
Py_XDECREF(ascii);

Py_INCREF(gai_req);
return (PyObject *)gai_req;
if (callback == Py_None) {
/* synchronous */
PyObject *dns_result;
err = pyuv__getaddrinfo_process_result(0, gai_req->req.addrinfo, &dns_result);
Py_DECREF(gai_req);
if (err < 0) {
RAISE_UV_EXCEPTION(err, PyExc_UVError);
return NULL;
}
return dns_result;
} else {
/* async */
Py_INCREF(gai_req);
return (PyObject *)gai_req;
}

error:
Py_XDECREF(idna);
Expand Down
17 changes: 13 additions & 4 deletions tests/test_dns.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,37 @@ class DnsTest(TestCase):
def test_getaddrinfo(self):
def getaddrinfo_cb(result, errorno):
self.assertEqual(errorno, None)
pyuv.dns.getaddrinfo(self.loop, getaddrinfo_cb, 'localhost', 80, socket.AF_INET)
pyuv.dns.getaddrinfo(self.loop, 'localhost', 80, socket.AF_INET, callback=getaddrinfo_cb)
self.loop.run()

def test_getaddrinfo_sync(self):
res = pyuv.dns.getaddrinfo(self.loop, 'localhost', 80, socket.AF_INET)
self.loop.run()
self.assertNotEqual(res, None)

def test_getaddrinfo_sync_fail(self):
self.assertRaises(pyuv.error.UVError, pyuv.dns.getaddrinfo, self.loop, 'lala.lala.lala', 80, socket.AF_INET)
self.loop.run()

def test_getaddrinfo_none(self):
def getaddrinfo_cb(result, errorno):
self.assertEqual(errorno, None)
self.assertEqual(result[0][4][1], 0)
pyuv.dns.getaddrinfo(self.loop, getaddrinfo_cb, 'localhost', None, socket.AF_INET)
pyuv.dns.getaddrinfo(self.loop, 'localhost', None, socket.AF_INET, callback=getaddrinfo_cb)
self.loop.run()

def test_getaddrinfo_service(self):
def getaddrinfo_cb(result, errorno):
self.assertEqual(errorno, None)
self.assertEqual(result[0][4][1], 80)
pyuv.dns.getaddrinfo(self.loop, getaddrinfo_cb, 'localhost', 'http', socket.AF_INET)
pyuv.dns.getaddrinfo(self.loop, 'localhost', 'http', socket.AF_INET, callback=getaddrinfo_cb)
self.loop.run()

def test_getaddrinfo_service_bytes(self):
def getaddrinfo_cb(result, errorno):
self.assertEqual(errorno, None)
self.assertEqual(result[0][4][1], 80)
pyuv.dns.getaddrinfo(self.loop, getaddrinfo_cb, b'localhost', b'http', socket.AF_INET)
pyuv.dns.getaddrinfo(self.loop, b'localhost', b'http', socket.AF_INET, callback=getaddrinfo_cb)
self.loop.run()

def test_getnameinfo_ipv4(self):
Expand Down

0 comments on commit 4c7f2c5

Please sign in to comment.