Ticket #2699: udp-reconnect-disconnect.2.patch

File udp-reconnect-disconnect.2.patch, 7.4 KB (added by Martin Gergov, 9 years ago)

forgot the C

  • twisted/topfiles/setup.py

     
    3535    Extension("twisted.python.sendmsg",
    3636              sources=["twisted/python/sendmsg.c"],
    3737              condition=lambda _: sys.platform != "win32"),
     38
     39    Extension("twisted.python.disconnect",
     40              sources=["twisted/python/disconnect.c"],
     41              condition=lambda _: sys.platform != "win32"),
    3842]
    3943
    4044if sys.version_info[:2] <= (2, 6):
  • twisted/internet/udp.py

     
    2424import warnings
    2525
    2626from zope.interface import implementer
     27from twisted.python.runtime import platformType
    2728
    28 from twisted.python.runtime import platformType
     29#Try to import C extension, if not disconnect will not work
     30try:
     31    from twisted.python.disconnect import disconnect_udp_sock
     32except ImportError:
     33    pass
     34
    2935if platformType == 'win32':
    3036    from errno import WSAEWOULDBLOCK
    3137    from errno import WSAEINTR, WSAEMSGSIZE, WSAETIMEDOUT
     
    205211        """
    206212        'Connect' to remote server.
    207213        """
    208         if self._connectedAddr:
    209             raise RuntimeError("already connected, reconnecting is not currently supported")
    210214        if not abstract.isIPAddress(host):
    211215            raise ValueError("please pass only IP addresses, not domain names")
    212216        self._connectedAddr = (host, port)
    213217        self.socket.connect((host, port))
    214218
     219    def disconnect(self):
     220        """
     221        'Disconnect' from remote server.
     222        Not to be confused with loseConnection. It will NOT call connectionLost.
     223        @returns: the result from the underlying syscall.
     224        """
     225        if not self._connectedAddr:
     226            raise RuntimeError("not connected, connect the socket first")
     227        self._connectedAddr = None
     228        return disconnect_udp_sock(self.fileno())
     229
    215230    def _loseConnection(self):
    216231        self.stopReading()
    217232        if self.connected: # actually means if we are *listening*
  • twisted/python/disconnect.c

     
     1/*
     2 * Copyright (c) Twisted Matrix Laboratories.
     3 * See LICENSE for details.
     4 */
     5#define PY_SSIZE_T_CLEAN 1
     6#include <Python.h>
     7
     8#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
     9/* This may cause some warnings, but if you want to get rid of them, upgrade
     10 * your Python version.  */
     11typedef int Py_ssize_t;
     12#endif
     13
     14#include <sys/types.h>
     15#include <sys/socket.h>
     16#include <signal.h>
     17#include <sys/param.h>
     18#include <netinet/in.h>
     19
     20#ifdef BSD
     21#include <sys/uio.h>
     22#endif
     23
     24PyObject *disconnect_socket_error;
     25static PyObject * disconnect_udp_sock(PyObject *self, PyObject *args,
     26                                      PyObject *keywds);
     27
     28static char disconnect_doc[] = "\
     29Bindings for disconnect in udp(in connected mode).\n\
     30";
     31
     32static char disconnect_disconnect_udp_sock_doc[] = "\
     33Wrap for disconnect function for udp in connected mode.\n\
     34\n\
     35@param fd: The file descriptor of the socket for disconnection.\n\
     36@type fd: C{int}\n\
     37\n\
     38@raise socket.error: Raised if the underlying syscall indicates an error.\n\
     39\n\
     40@return: Value of the underlying syscall(connect(2)), if it succeeds.\n\
     41";
     42
     43
     44static PyMethodDef disconnect_methods[] = {
     45    {"disconnect_udp_sock", (PyCFunction) disconnect_udp_sock,
     46     METH_VARARGS | METH_KEYWORDS,
     47     disconnect_disconnect_udp_sock_doc},
     48    {NULL, NULL, 0, NULL}
     49};
     50
     51
     52PyMODINIT_FUNC initdisconnect(void) {
     53    PyObject *module;
     54
     55    disconnect_socket_error = NULL; /* Make sure that this has a known value
     56                                    before doing anything that might exit. */
     57
     58    module = Py_InitModule3("disconnect", disconnect_methods, disconnect_doc);
     59
     60    if (!module) {
     61        return;
     62    }
     63
     64    /*
     65      The following is the only value mentioned by POSIX:
     66      http://www.opengroup.org/onlinepubs/9699919799/basedefs/sys_socket.h.html
     67    */
     68
     69    if (-1 == PyModule_AddIntConstant(module, "SCM_RIGHTS", SCM_RIGHTS)) {
     70        return;
     71    }
     72
     73
     74    /* BSD, Darwin, Hurd */
     75#if defined(SCM_CREDS)
     76    if (-1 == PyModule_AddIntConstant(module, "SCM_CREDS", SCM_CREDS)) {
     77        return;
     78    }
     79#endif
     80
     81    /* Linux */
     82#if defined(SCM_CREDENTIALS)
     83    if (-1 == PyModule_AddIntConstant(module, "SCM_CREDENTIALS", SCM_CREDENTIALS)) {
     84        return;
     85    }
     86#endif
     87
     88    /* Apparently everywhere, but not standardized. */
     89#if defined(SCM_TIMESTAMP)
     90    if (-1 == PyModule_AddIntConstant(module, "SCM_TIMESTAMP", SCM_TIMESTAMP)) {
     91        return;
     92    }
     93#endif
     94
     95    module = PyImport_ImportModule("socket");
     96    if (!module) {
     97        return;
     98    }
     99
     100    disconnect_socket_error = PyObject_GetAttrString(module, "error");
     101    if (!disconnect_socket_error) {
     102        return;
     103    }
     104}
     105
     106
     107/* Taken from
     108 * http://timesinker.blogspot.com/2010/02/unconnect-udp-socket.html */
     109
     110static PyObject * disconnect_udp_sock(PyObject *self, PyObject *args,
     111                                      PyObject *keywds)
     112{
     113    int fd;
     114    static char *kwlist[] = {"fd", NULL};
     115    struct sockaddr_in sin;
     116
     117    if (!PyArg_ParseTupleAndKeywords(args, keywds, "i", kwlist, &fd)) {
     118        return NULL;
     119    }
     120
     121    memset((char *)&sin, 0, sizeof(sin));
     122    sin.sin_family = AF_UNSPEC;
     123    return Py_BuildValue("i", connect(fd, (struct sockaddr *)&sin,
     124                                      sizeof(sin)));
     125}
  • twisted/test/test_udp.py

     
    314314        """
    315315        A call to the transport's connect method fails with a L{ValueError}
    316316        when a non-IP address is passed as the host value.
    317 
    318         A call to a transport's connect method fails with a L{RuntimeError}
    319         when the transport is already connected.
    320317        """
    321318        client = GoodClient()
    322319        port = reactor.listenUDP(0, client, interface="127.0.0.1")
    323320        self.assertRaises(ValueError, client.transport.connect,
    324321                          "localhost", 80)
     322       
     323        return port.stopListening()
     324
     325    def test_disconnect(self):
     326        """
     327        A call to the transport's disconnect method before connecting raises
     328        a L{RuntimeError}, because we're not connected yet.
     329
     330        When connected to a remote server we can disconnect.
     331        Note: optional test(if extensions are built it will run).
     332        """
     333        client = GoodClient()
     334        port = reactor.listenUDP(0, client, interface="127.0.0.1")
     335        self.assertRaises(RuntimeError, client.transport.disconnect)
     336
    325337        client.transport.connect("127.0.0.1", 80)
    326         self.assertRaises(RuntimeError, client.transport.connect,
    327                           "127.0.0.1", 80)
     338        try:
     339            res = client.transport.disconnect()
     340        except NameError as e:
     341            if "disconnect" in e.message:
     342                port.stopListening()
     343                raise unittest.SkipTest("cannot test without "+\
     344                                            "disconnect extension")
     345            raise
     346        else:
     347            self.assertEqual(client.transport._connectedAddr, None)
     348            self.assertEqual(res, 0)
     349
    328350        return port.stopListening()
    329351
    330352