some recv magic, still no PyString_Resize, though
authorTero Marttila <terom@fixme.fi>
Sun, 16 Aug 2009 23:32:00 +0300
changeset 14 c44754cc1ffe
parent 13 a1091632a8a7
child 15 8e7037cc05c7
some recv magic, still no PyString_Resize, though
qmsk/net/libc.pxd
qmsk/net/py.pxd
qmsk/net/socket/platform.pxd
qmsk/net/socket/socket.pyx
--- a/qmsk/net/libc.pxd	Sun Aug 16 21:54:46 2009 +0300
+++ b/qmsk/net/libc.pxd	Sun Aug 16 23:32:00 2009 +0300
@@ -42,6 +42,7 @@
     int close (int fd)
 
 cdef extern from "alloca.h" :
+    # XXX: this is unsafe because there is it may fail nastily for allocations that won't fit on the stack - SIGSEGV
     void* alloca (size_t size)
 
 cdef extern from "sys/uio.h" :
--- a/qmsk/net/py.pxd	Sun Aug 16 21:54:46 2009 +0300
+++ b/qmsk/net/py.pxd	Sun Aug 16 23:32:00 2009 +0300
@@ -1,17 +1,25 @@
-cimport qmsk.net.libc as libc
+from qmsk.net.libc cimport ssize_t, errno
 
 cdef extern from "Python.h" :
     struct PyObject :
         pass
 
     ## string stuff
-    int PyObject_AsCharBuffer (object obj, char **buf, libc.ssize_t *len) except -1
+    int PyObject_AsCharBuffer (object obj, char **buf, ssize_t *len) except -1
+
+    object PyString_FromStringAndSize (char *v, ssize_t len)
+    char* PyString_AS_STRING (object) except NULL
+    int _PyString_Resize (PyObject **string, ssize_t newsize) except -1
     
     ## except setting
     PyObject* PyErr_SetFromErrno (PyObject *type)
 
     PyObject *PyExc_OSError
     
+## custom haxes
+#cdef extern from "_py.c" :
+#    object py_string_resize (object string, ssize_t new_size)
+
 # raise OSError with errno
 # XXX: doesn't do anything with func
 cdef int raise_errno (char *func) except -1
--- a/qmsk/net/socket/platform.pxd	Sun Aug 16 21:54:46 2009 +0300
+++ b/qmsk/net/socket/platform.pxd	Sun Aug 16 23:32:00 2009 +0300
@@ -120,7 +120,7 @@
     ssize_t send (int fd, void *buf, size_t n, int flags)
     ssize_t recv (int fd, void *buf, size_t n, int flags)
     ssize_t sendto (int fd, void *buf, size_t n, int flags, sockaddr *addr, socklen_t addr_len)
-    ssize_t recvfrom (int fd, void *buf, size_t n, int flags, sockaddr *addr, socklen_t addr_len)
+    ssize_t recvfrom (int fd, void *buf, size_t n, int flags, sockaddr *addr, socklen_t *addr_len)
     ssize_t sendmsg (int fd, msghdr *msg, int flags)
     ssize_t recvmsg (int fd, msghdr *msg, int flags)
 
--- a/qmsk/net/socket/socket.pyx	Sun Aug 16 21:54:46 2009 +0300
+++ b/qmsk/net/socket/socket.pyx	Sun Aug 16 23:32:00 2009 +0300
@@ -22,6 +22,7 @@
 
     if buf is not None :
         # XXX: test that except works right
+        # XXX: this complains about const...
         py.PyObject_AsCharBuffer(buf, <char **> buf_ptr, &tmp_len)
         
         # XXX: ensure that this is >= 0
@@ -66,6 +67,13 @@
         if self.fd < 0 :
             raise_errno('socket')
 
+    def fileno (self) :
+        """
+            Returns the OS-level file desriptor for this socket as an integer
+        """
+
+        return self.fd
+
     def bind (self, sockaddr addr) :
         """
             Bind this socket to the given local socket address. The given sockaddr should be of the same or a
@@ -128,19 +136,17 @@
             socket must be a connection-based socket (SOCK_STREAM/SOCK_SEQPACKET) and in the passive listening mode
             (.listen()).
 
-            This returns a (sock, sockaddr) tuple:
+            This returns a (sock, src_addr) tuple:
                 sock        - the newly created sock, corresponding to the incoming connection
-                sockaddr    - the remote address of the incoming connection
+                src_addr    - the remote address of the incoming connection
         """
         
         # prep the sockaddr that we will return
         cdef platform.sockaddr_storage ss
         cdef platform.socklen_t ss_len = sizeof(ss)
 
-        cdef socket_t sock_fd
-
         # accept()
-        sock_fd = platform.accept(self.fd, <platform.sockaddr *> &ss, &ss_len)
+        cdef socket_t sock_fd = platform.accept(self.fd, <platform.sockaddr *> &ss, &ss_len)
 
         if sock_fd < 0 :
             raise_errno('accept')
@@ -156,9 +162,9 @@
             raise
 
         # prep the new addr
-        sock_addr = build_sockaddr(<platform.sockaddr *> &ss, ss_len)
+        cdef sockaddr src_addr = build_sockaddr(<platform.sockaddr *> &ss, ss_len)
 
-        return sock_obj, sock_addr
+        return sock_obj, src_addr
 
     def send (self, object buf, int flags = 0) :
         """
@@ -323,7 +329,131 @@
 
         else :
             return ret
+    
+    def recv (self, size_t len, int flags = 0) :
+        """
+            Recieve a message, reading and returning at most `len` bytes.
 
+                len         - size of buffer to use for recv
+                flags       - (optional) MSG_* flags to use for recv()
+
+            Returns the recieved data as a newly allocated string of the correct length.
+        """
+        
+        # alloc a new return str
+        # XXX: len overflow...
+        cdef object str = py.PyString_FromStringAndSize(NULL, len)
+        cdef char *buf = py.PyString_AS_STRING(str)
+        
+        # recv()
+        cdef libc.ssize_t ret = platform.recv(self.fd, buf, len, flags)
+
+        if ret < 0 :
+            raise_errno('recv')
+        
+        # XXX: figure out how to call _PyString_Resize
+        return str[:ret]
+    
+    def recvfrom (self, size_t len, int flags = 0) :
+        """
+            Recieve a message, reading at most `len` bytes, also returning the source address.
+
+                len         - size of buffer to use for recv
+                flags       - (optional) MSG_* flags to use for recvfrom()
+            
+            Returns the recieved data and the source address as a (buf, src_addr) tuple :
+                buf         - a newly allocated string containing the recieved data, of the correct length
+                src_addr    - the source address the message was recieved from
+        """
+
+        # alloc a new return str
+        # XXX: len overflow...
+        cdef object str = py.PyString_FromStringAndSize(NULL, len)
+        cdef char *buf = py.PyString_AS_STRING(str)
+        
+        # prep the sockaddr that we will return
+        cdef platform.sockaddr_storage ss
+        cdef platform.socklen_t ss_len = sizeof(ss)
+        
+        # recvfrom()
+        cdef libc.ssize_t ret = platform.recvfrom(self.fd, buf, len, flags, <platform.sockaddr *> &ss, &ss_len)
+
+        if ret < 0 :
+            raise_errno('recv')
+        
+        # prep the new addr
+        cdef sock_addr = build_sockaddr(<platform.sockaddr *> &ss, ss_len)
+        
+        # XXX: figure out how to call _PyString_Resize
+        return str[:ret], sock_addr
+    
+    def recvmsg (self, bint recv_addr = True, object iov_lens = None, size_t control_len = 0, int flags = 0) :
+        """
+            Recieve a message along with some extra data.
+
+                recv_addr   - ask for and return a sockaddr for the source address of the message?
+                lenv        - (optional) sequence of buffer sizes to use for the message data iov
+                control_len - (optional) amount of auxiliary data to recieve
+                flags       - (optional) flags to pass to recvmsg()
+
+            Returns a (name, iov, control, flags) tuple :
+                name        - the source address of the message, or None
+                iov         - sequence of strings containing the recieved data, each at most lenv[x] bytes long
+                control     - string containing recieved control message, if any
+                flags       - recieved flags
+        """
+
+        cdef platform.msghdr msg
+
+        libc.memset(&msg, 0, sizeof(msg))
+
+        # prep the sockaddr that we will return
+        cdef platform.sockaddr_storage ss
+        
+        # ask for a name?
+        if recv_addr :
+            msg.msg_name = <void *> &ss
+            msg.msg_namelen = sizeof(ss)
+
+        # build iov?
+        if iov_lens :
+            # XXX: implement
+            pass
+
+        # build control buffer?
+        if control_len :
+            # XXX: implement
+            pass
+
+        # recvmsg()
+        cdef libc.ssize_t ret = platform.recvmsg(self.fd, &msg, flags)
+
+        if ret < 0 :
+            raise_errno('recvmsg')
+        
+        # name?
+        cdef sockaddr name = None
+
+        if msg.msg_name and msg.msg_namelen :
+            name = build_sockaddr(<platform.sockaddr *> msg.msg_name, msg.msg_namelen)
+
+        # iov?
+        cdef object iov = None
+
+        if ret :
+            assert msg.msg_iov and msg.msg_iovlen
+            
+            # XXX: implement
+            pass
+
+        # control?
+        cdef object control = None
+
+        if msg.msg_control and msg.msg_controllen :
+            # XXX: implement
+            pass
+
+        return name, iov, control, msg.msg_flags
 
     def shutdown (self, how) :
         """