~ubuntu-branches/debian/sid/pyro/sid

« back to all changes in this revision

Viewing changes to src/Pyro/socketserver/selectserver.py

  • Committer: Bazaar Package Importer
  • Author(s): Carl Chenet, Carl Chenet, Jakub Wilk
  • Date: 2010-09-14 01:04:28 UTC
  • Revision ID: james.westby@ubuntu.com-20100914010428-02r7p1rzr7jvw94z
Tags: 1:3.9.1-2
[Carl Chenet]
* revert to 3.9.1-1 package because of the development status 
  of the 4.1 package is unsuitable for stable use
  DPMT svn #8557 revision (Closes: #589172) 
* added debian/source
* added debian/source/format
* package is now 3.0 (quilt) source format
* debian/control
  - Bump Standards-Version to 3.9.1

[Jakub Wilk]
* Add ‘XS-Python-Version: >= 2.5’ to prevent bytecompilation with python2.4
  (closes: #589053).

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
"""
2
 
Socket server based on select/poll. Doesn't use threads.
3
 
 
4
 
Not supported for Jython due to a few obscure problems
5
 
with the select/poll implementation.
6
 
 
7
 
Pyro - Python Remote Objects.  Copyright by Irmen de Jong.
8
 
irmen@razorvine.net - http://www.razorvine.net/python/Pyro
9
 
"""
10
 
 
11
 
import select, os, socket, logging
12
 
from Pyro.socketutil import SocketConnection, createSocket, ERRNO_RETRIES, ERRNO_BADF
13
 
from Pyro.errors import ConnectionClosedError, PyroError
14
 
import Pyro.config
15
 
 
16
 
log=logging.getLogger("Pyro.socketserver.select")
17
 
 
18
 
class SocketServer_Select(object):
19
 
    """transport server for socket connections, select/poll loop multiplex version."""
20
 
    def __init__(self, callbackObject, host, port, timeout=None):
21
 
        if os.name=="java":
22
 
            raise NotImplementedError("select-based server is not supported for Jython, use the threadpool server instead")
23
 
        log.info("starting select/poll socketserver")
24
 
        self.sock=None
25
 
        self.sock=createSocket(bind=(host,port), timeout=timeout)
26
 
        self.clients=[]
27
 
        self.callback=callbackObject
28
 
        sockaddr=self.sock.getsockname()
29
 
        if sockaddr[0].startswith("127."):
30
 
            if host is None or host.lower()!="localhost" and not host.startswith("127."):
31
 
                log.warn("weird DNS setup: %s resolves to localhost (127.x.x.x)",host)
32
 
        host=host or sockaddr[0]
33
 
        port=port or sockaddr[1]
34
 
        self.locationStr="%s:%d" % (host,port)
35
 
    def __del__(self):
36
 
        if self.sock is not None:
37
 
            self.sock.close()
38
 
            self.sock=None
39
 
    if hasattr(select,"poll"):
40
 
        def requestLoop(self, loopCondition=lambda:True):
41
 
            log.debug("enter poll-based requestloop")
42
 
            try:
43
 
                poll=select.poll()  #@UndefinedVariable (pydev)
44
 
                fileno2connection={}  # map fd to original connection object
45
 
                poll.register(self.sock.fileno(), select.POLLIN | select.POLLPRI) #@UndefinedVariable (pydev)
46
 
                fileno2connection[self.sock.fileno()]=self.sock
47
 
                while loopCondition():
48
 
                    polls=poll.poll(1000*Pyro.config.POLLTIMEOUT)
49
 
                    for (fd,mask) in polls:  #@UnusedVariable (pydev)
50
 
                        conn=fileno2connection[fd]
51
 
                        if conn is self.sock:
52
 
                            try:
53
 
                                conn=self.handleConnection(self.sock)
54
 
                            except ConnectionClosedError:
55
 
                                log.info("server socket was closed, stopping requestloop")
56
 
                                return
57
 
                            if conn:
58
 
                                poll.register(conn.fileno(), select.POLLIN | select.POLLPRI) #@UndefinedVariable (pydev)
59
 
                                fileno2connection[conn.fileno()]=conn
60
 
                        else:
61
 
                            try:
62
 
                                self.callback.handleRequest(conn)
63
 
                            except (socket.error,ConnectionClosedError):
64
 
                                # client went away.
65
 
                                try:
66
 
                                    fn=conn.fileno()
67
 
                                except socket.error:
68
 
                                    pass  
69
 
                                else:
70
 
                                    poll.unregister(fn)
71
 
                                    del fileno2connection[fn]
72
 
                                    conn.close()
73
 
            except KeyboardInterrupt:
74
 
                log.debug("stopping on break signal")
75
 
                pass
76
 
            finally:
77
 
                if hasattr(poll, "close"):
78
 
                    poll.close()
79
 
            log.debug("exit poll-based requestloop")
80
 
 
81
 
    else:
82
 
        def requestLoop(self, loopCondition=lambda:True):
83
 
            log.debug("entering select-based requestloop")
84
 
            while loopCondition():
85
 
                try:
86
 
                    rlist=self.clients[:]
87
 
                    rlist.append(self.sock)
88
 
                    try:
89
 
                        rlist,_,_=select.select(rlist, [], [], Pyro.config.POLLTIMEOUT)
90
 
                    except select.error:
91
 
                        if loopCondition():
92
 
                            raise
93
 
                        else:
94
 
                            # swallow the select error if the loopcondition is no longer true, and exit loop
95
 
                            # this can occur if we are shutting down and the socket is no longer valid
96
 
                            break
97
 
                    if self.sock in rlist:
98
 
                        rlist.remove(self.sock)
99
 
                        try:
100
 
                            conn=self.handleConnection(self.sock)
101
 
                            if conn:
102
 
                                self.clients.append(conn)
103
 
                        except ConnectionClosedError:
104
 
                            log.info("server socket was closed, stopping requestloop")
105
 
                            return
106
 
                    for conn in rlist[:]:
107
 
                        if conn in self.clients:
108
 
                            rlist.remove(conn)
109
 
                            try:
110
 
                                if self.callback:
111
 
                                    self.callback.handleRequest(conn)
112
 
                            except (socket.error,ConnectionClosedError):
113
 
                                # client went away.
114
 
                                conn.close()
115
 
                                if conn in self.clients:
116
 
                                    self.clients.remove(conn)
117
 
                except socket.timeout:
118
 
                    pass   # just continue the loop on a timeout
119
 
                except KeyboardInterrupt:
120
 
                    log.debug("stopping on break signal")
121
 
                    break
122
 
            log.debug("exit select-based requestloop")
123
 
 
124
 
    def handleRequests(self, eventsockets):
125
 
        """used for external event loops: handle events that occur on one of the sockets of this server"""
126
 
        for s in eventsockets:
127
 
            if s is self.sock:
128
 
                # server socket, means new connection
129
 
                conn=self.handleConnection(self.sock)
130
 
                if conn:
131
 
                    self.clients.append(conn)
132
 
            else:
133
 
                # must be client socket, means remote call
134
 
                try:
135
 
                    if self.callback:
136
 
                        self.callback.handleRequest(s)
137
 
                except (socket.error,ConnectionClosedError):
138
 
                    # client went away.
139
 
                    s.close()
140
 
                    if s in self.clients:
141
 
                        self.clients.remove(s)
142
 
 
143
 
    def handleConnection(self, sock):
144
 
        try:
145
 
            csock, caddr=sock.accept()
146
 
            log.debug("connection from %s",caddr)
147
 
            if Pyro.config.COMMTIMEOUT:
148
 
                csock.settimeout(Pyro.config.COMMTIMEOUT)
149
 
        except socket.error,x:
150
 
            err=getattr(x,"errno",x.args[0])
151
 
            if err in ERRNO_RETRIES:
152
 
                # just ignore this error for now and continue
153
 
                log.warn("accept() failed errno=%d, shouldn't happen", err)
154
 
                return None
155
 
            if err in ERRNO_BADF:
156
 
                # our server socket got destroyed
157
 
                raise ConnectionClosedError("server socket closed")
158
 
            raise
159
 
        try:
160
 
            conn=SocketConnection(csock)
161
 
            if self.callback.handshake(conn):
162
 
                return conn
163
 
        except (socket.error, PyroError), x:
164
 
            log.warn("error during connect: %s",x)
165
 
            csock.close()
166
 
        return None
167
 
 
168
 
    def close(self): 
169
 
        log.debug("closing socketserver")
170
 
        if self.sock:
171
 
            self.sock.close()
172
 
        self.sock=None
173
 
        for c in self.clients:
174
 
            try:
175
 
                c.close()
176
 
            except Exception:
177
 
                pass
178
 
        self.clients=[]
179
 
 
180
 
    def fileno(self):
181
 
        return self.sock.fileno()
182
 
    def sockets(self):
183
 
        socks=[self.sock]
184
 
        socks.extend(self.clients)
185
 
        return socks
186
 
 
187
 
    def pingConnection(self):
188
 
        """bit of a hack to trigger a blocking server to get out of the loop, useful at clean shutdowns"""
189
 
        try:
190
 
            self.sock.send("!!!!!!!!!!!!!!!!!!!!!!!")
191
 
        except socket.error:
192
 
            pass