# HG changeset patch # User Tero Marttila # Date 1250448886 -10800 # Node ID a1091632a8a7675c8fed407b2b494d1b9e2341ff # Parent 314d47bdd4d99a211eb96cf45a7449a2d0dbc2a4 implement __cinit__, shutdown, close, __dealloc__ for socket, and also add a try-except to not leak client sock from accept() diff -r 314d47bdd4d9 -r a1091632a8a7 qmsk/net/libc.pxd --- a/qmsk/net/libc.pxd Sun Aug 16 21:13:36 2009 +0300 +++ b/qmsk/net/libc.pxd Sun Aug 16 21:54:46 2009 +0300 @@ -39,6 +39,8 @@ ssize_t read (int fd, void *buf, size_t count) ssize_t write (int fd, void *buf, size_t count) + int close (int fd) + cdef extern from "alloca.h" : void* alloca (size_t size) diff -r 314d47bdd4d9 -r a1091632a8a7 qmsk/net/socket/socket.pxd --- a/qmsk/net/socket/socket.pxd Sun Aug 16 21:13:36 2009 +0300 +++ b/qmsk/net/socket/socket.pxd Sun Aug 16 21:54:46 2009 +0300 @@ -9,16 +9,25 @@ Represents a single OS-level socket >>> from qmsk.net.socket import addr - >>> s = socket() + >>> from qmsk.net.socket.constants import * + + >>> s = socket(1337) + Traceback (most recent call last): + ... + OSError: [Errno 97] Address family not supported by protocol + + >>> s = socket(fd=1337) >>> s.send('foo') Traceback (most recent call last): ... OSError: [Errno 9] Bad file descriptor - >>> s.socket() + + >>> s = socket(AF_INET, SOCK_STREAM) >>> s.bind(addr.sockaddr_in('127.0.0.1', 1337)) >>> s.listen(1) >>> s.listen(0) - >>> s = socket(); s.socket() + + >>> s = socket() >>> s.connect(addr.sockaddr_in('127.0.0.1', 1338)) Traceback (most recent call last): ... diff -r 314d47bdd4d9 -r a1091632a8a7 qmsk/net/socket/socket.pyx --- a/qmsk/net/socket/socket.pyx Sun Aug 16 21:13:36 2009 +0300 +++ b/qmsk/net/socket/socket.pyx Sun Aug 16 21:54:46 2009 +0300 @@ -37,27 +37,30 @@ # XXX: do some GIL-releasin' cdef class socket : - def __init__ (self, int fd = -1) : + def __cinit__ (self) : """ - Construct this socket with the given fd, or -1 to mark it as fd-less + Initialize the socket to set fd to -1, so that we dont't try and close stdin too often :) """ - self.fd = fd + self.fd = -1 - def socket (self, int family = platform.AF_INET, int socktype = platform.SOCK_STREAM, int protocol = 0) : + def __init__ (self, int family = platform.AF_INET, int socktype = platform.SOCK_STREAM, int protocol = 0, int fd = -1) : """ - Create a new socket endpoint with the given family/domain, socktype and optionally, specific protocol. + Create a new socket endpoint with the given family/domain, socktype and optionally, specific protocol, + unless the fd argument is given as >= 0, in which case it used directly. family - one of AF_* socktype - one of SOCK_* protocol - one of IPPROTO_* or zero to select default """ - if self.fd >= 0 : - raise Exception("Socket fd already exists") - - # socket() - self.fd = platform.socket(family, socktype, protocol) + if fd >= 0 : + # given fd + self.fd = fd + + else : + # socket() + self.fd = platform.socket(family, socktype, protocol) # trap if self.fd < 0 : @@ -141,9 +144,16 @@ if sock_fd < 0 : raise_errno('accept') + + try : + # prep the new socket + sock_obj = socket(sock_fd) - # prep the new socket - sock_obj = socket(sock_fd) + except : + # XXX: don't leak the socket fd? How does socket.__init__ handle this? + platform.close(sock_fd) + + raise # prep the new addr sock_addr = build_sockaddr( &ss, ss_len) @@ -303,7 +313,6 @@ for i, buf in enumerate(iov) : iovec = &iov_list[i] - parse_buf(&iovec.iov_base, &iovec.iov_len, buf, 1) # sendmsg() @@ -314,4 +323,58 @@ else : return ret - + + + def shutdown (self, how) : + """ + Shutdown part of a full-duplex connection. + + how - one of SHUT_* + + This does not affect this socket's fd. + """ + + # shutdown() + if platform.shutdown(self.fd, how) : + raise_errno('shutdown') + + def close (self) : + """ + Close the socket fd if we have one, invalidating it if succesful. + + Note that this will raise an error and keep the fd if the system close() returns an error. + + Calling this again after a succesfull close() does nothing. + + XXX: SO_LINGER/blocking? + + >>> s = socket() + >>> s.fd >= 0 + True + >>> s.close() + >>> s.fd >= 0 + False + >>> s.close() + """ + + # ignore if already closed + if self.fd < 0 : + return + + # close() + if libc.close(self.fd) : + raise_errno('close') + + # invalidate + self.fd = -1 + + def __dealloc__ (self) : + """ + Close the socket fd if one is set, ignoring any errors from close + """ + + if self.fd >= 0 : + if libc.close(self.fd) : + # XXX: at least warn... ? + pass +