Annotation of ECHO_content/timeoutsocket.py, revision 1.2
1.1 dwinter 1:
2: ####
3: # Copyright 2000,2001 by Timothy O'Malley <timo@alum.mit.edu>
4: #
5: # All Rights Reserved
6: #
7: # Permission to use, copy, modify, and distribute this software
8: # and its documentation for any purpose and without fee is hereby
9: # granted, provided that the above copyright notice appear in all
10: # copies and that both that copyright notice and this permission
11: # notice appear in supporting documentation, and that the name of
12: # Timothy O'Malley not be used in advertising or publicity
13: # pertaining to distribution of the software without specific, written
14: # prior permission.
15: #
16: # Timothy O'Malley DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17: # SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18: # AND FITNESS, IN NO EVENT SHALL Timothy O'Malley BE LIABLE FOR
19: # ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20: # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
21: # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
22: # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
23: # PERFORMANCE OF THIS SOFTWARE.
24: #
25: ####
26:
27: """Timeout Socket
28:
29: This module enables a timeout mechanism on all TCP connections. It
30: does this by inserting a shim into the socket module. After this module
31: has been imported, all socket creation goes through this shim. As a
32: result, every TCP connection will support a timeout.
33:
34: The beauty of this method is that it immediately and transparently
35: enables the entire python library to support timeouts on TCP sockets.
36: As an example, if you wanted to SMTP connections to have a 20 second
37: timeout:
38:
39: import timeoutsocket
40: import smtplib
41: timeoutsocket.setDefaultSocketTimeout(20)
42:
43:
44: The timeout applies to the socket functions that normally block on
45: execution: read, write, connect, and accept. If any of these
46: operations exceeds the specified timeout, the exception Timeout
47: will be raised.
48:
49: The default timeout value is set to None. As a result, importing
50: this module does not change the default behavior of a socket. The
51: timeout mechanism only activates when the timeout has been set to
52: a numeric value. (This behavior mimics the behavior of the
53: select.select() function.)
54:
55: This module implements two classes: TimeoutSocket and TimeoutFile.
56:
57: The TimeoutSocket class defines a socket-like object that attempts to
58: avoid the condition where a socket may block indefinitely. The
59: TimeoutSocket class raises a Timeout exception whenever the
60: current operation delays too long.
61:
62: The TimeoutFile class defines a file-like object that uses the TimeoutSocket
63: class. When the makefile() method of TimeoutSocket is called, it returns
64: an instance of a TimeoutFile.
65:
66: Each of these objects adds two methods to manage the timeout value:
67:
68: get_timeout() --> returns the timeout of the socket or file
69: set_timeout() --> sets the timeout of the socket or file
70:
71:
72: As an example, one might use the timeout feature to create httplib
73: connections that will timeout after 30 seconds:
74:
75: import timeoutsocket
76: import httplib
77: H = httplib.HTTP("www.python.org")
78: H.sock.set_timeout(30)
79:
80: Note: When used in this manner, the connect() routine may still
81: block because it happens before the timeout is set. To avoid
82: this, use the 'timeoutsocket.setDefaultSocketTimeout()' function.
83:
84: Good Luck!
85:
86: """
87:
1.2 ! dwinter 88: __version__ = "$Revision: 1.1 $"
1.1 dwinter 89: __author__ = "Timothy O'Malley <timo@alum.mit.edu>"
90:
91: #
92: # Imports
93: #
94: import select, string
95: import socket
96: if not hasattr(socket, "_no_timeoutsocket"):
97: _socket = socket.socket
98: else:
99: _socket = socket._no_timeoutsocket
100:
101: #
102: # Set up constants to test for Connected and Blocking operations.
103: # We delete 'os' and 'errno' to keep our namespace clean(er).
104: # Thanks to Alex Martelli and G. Li for the Windows error codes.
105: #
106: import os
107: if os.name == "nt":
108: _IsConnected = ( 10022, 10056 )
109: _ConnectBusy = ( 10035, )
110: _AcceptBusy = ( 10035, )
111: else:
112: import errno
113: _IsConnected = ( errno.EISCONN, )
114: _ConnectBusy = ( errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK )
115: _AcceptBusy = ( errno.EAGAIN, errno.EWOULDBLOCK )
116: del errno
117: del os
118:
119:
120: #
121: # Default timeout value for ALL TimeoutSockets
122: #
123: _DefaultTimeout = None
124: def setDefaultSocketTimeout(timeout):
125: global _DefaultTimeout
126: _DefaultTimeout = timeout
127: def getDefaultSocketTimeout():
128: return _DefaultTimeout
129:
130: #
131: # Exceptions for socket errors and timeouts
132: #
133: Error = socket.error
134: class Timeout(Exception):
135: pass
136:
137:
138: #
139: # Factory function
140: #
141: from socket import AF_INET, SOCK_STREAM
142: def timeoutsocket(family=AF_INET, type=SOCK_STREAM, proto=None):
143: if family != AF_INET or type != SOCK_STREAM:
1.2 ! dwinter 144:
1.1 dwinter 145: if proto:
146: return _socket(family, type, proto)
147: else:
148: return _socket(family, type)
149: return TimeoutSocket( _socket(family, type), _DefaultTimeout )
150: # end timeoutsocket
151:
152: #
153: # The TimeoutSocket class definition
154: #
155: class TimeoutSocket:
156: """TimeoutSocket object
157: Implements a socket-like object that raises Timeout whenever
158: an operation takes too long.
159: The definition of 'too long' can be changed using the
160: set_timeout() method.
161: """
162:
163: _copies = 0
164: _blocking = 1
165:
166: def __init__(self, sock, timeout):
167: self._sock = sock
168: self._timeout = timeout
169: # end __init__
170:
171: def __getattr__(self, key):
172: return getattr(self._sock, key)
173: # end __getattr__
174:
175: def get_timeout(self):
176: return self._timeout
177: # end set_timeout
178:
179: def set_timeout(self, timeout=None):
180: self._timeout = timeout
181: # end set_timeout
182:
183: def setblocking(self, blocking):
184: self._blocking = blocking
185: return self._sock.setblocking(blocking)
186: # end set_timeout
187:
188: def connect_ex(self, addr):
189: errcode = 0
190: try:
191: self.connect(addr)
192: except Error, why:
193: errcode = why[0]
194: return errcode
195: # end connect_ex
196:
197: def connect(self, addr, port=None, dumbhack=None):
198: # In case we were called as connect(host, port)
199: if port != None: addr = (addr, port)
200:
201: # Shortcuts
202: sock = self._sock
203: timeout = self._timeout
204: blocking = self._blocking
205:
206: # First, make a non-blocking call to connect
207: try:
208: sock.setblocking(0)
209: sock.connect(addr)
210: sock.setblocking(blocking)
211: return
212: except Error, why:
213: # Set the socket's blocking mode back
214: sock.setblocking(blocking)
215:
216: # If we are not blocking, re-raise
217: if not blocking:
218: raise
219:
220: # If we are already connected, then return success.
221: # If we got a genuine error, re-raise it.
222: errcode = why[0]
223: if dumbhack and errcode in _IsConnected:
224: return
225: elif errcode not in _ConnectBusy:
226: raise
227:
228: # Now, wait for the connect to happen
229: # ONLY if dumbhack indicates this is pass number one.
230: # If select raises an error, we pass it on.
231: # Is this the right behavior?
232: if not dumbhack:
233: r,w,e = select.select([], [sock], [], timeout)
234: if w:
235: return self.connect(addr, dumbhack=1)
236:
237: # If we get here, then we should raise Timeout
238: raise Timeout("Attempted connect to %s timed out." % str(addr) )
239: # end connect
240:
241: def accept(self, dumbhack=None):
242: # Shortcuts
243: sock = self._sock
244: timeout = self._timeout
245: blocking = self._blocking
246:
247: # First, make a non-blocking call to accept
248: # If we get a valid result, then convert the
249: # accept'ed socket into a TimeoutSocket.
250: # Be carefult about the blocking mode of ourselves.
251: try:
252: sock.setblocking(0)
253: newsock, addr = sock.accept()
254: sock.setblocking(blocking)
255: timeoutnewsock = self.__class__(newsock, timeout)
256: timeoutnewsock.setblocking(blocking)
257: return (timeoutnewsock, addr)
258: except Error, why:
259: # Set the socket's blocking mode back
260: sock.setblocking(blocking)
261:
262: # If we are not supposed to block, then re-raise
263: if not blocking:
264: raise
265:
266: # If we got a genuine error, re-raise it.
267: errcode = why[0]
268: if errcode not in _AcceptBusy:
269: raise
270:
271: # Now, wait for the accept to happen
272: # ONLY if dumbhack indicates this is pass number one.
273: # If select raises an error, we pass it on.
274: # Is this the right behavior?
275: if not dumbhack:
276: r,w,e = select.select([sock], [], [], timeout)
277: if r:
278: return self.accept(dumbhack=1)
279:
280: # If we get here, then we should raise Timeout
281: raise Timeout("Attempted accept timed out.")
282: # end accept
283:
284: def send(self, data, flags=0):
285: sock = self._sock
286: if self._blocking:
287: r,w,e = select.select([],[sock],[], self._timeout)
288: if not w:
289: raise Timeout("Send timed out")
290: return sock.send(data, flags)
291: # end send
292:
293: def recv(self, bufsize, flags=0):
294: sock = self._sock
295: if self._blocking:
296: r,w,e = select.select([sock], [], [], self._timeout)
297: if not r:
298: raise Timeout("Recv timed out")
299: return sock.recv(bufsize, flags)
300: # end recv
301:
302: def makefile(self, flags="r", bufsize=-1):
303: self._copies = self._copies +1
304: return TimeoutFile(self, flags, bufsize)
305: # end makefile
306:
307: def close(self):
308: if self._copies <= 0:
309: self._sock.close()
310: else:
311: self._copies = self._copies -1
312: # end close
313:
314: # end TimeoutSocket
315:
316:
317: class TimeoutFile:
318: """TimeoutFile object
319: Implements a file-like object on top of TimeoutSocket.
320: """
321:
322: def __init__(self, sock, mode="r", bufsize=4096):
323: self._sock = sock
324: self._bufsize = 4096
325: if bufsize > 0: self._bufsize = bufsize
326: if not hasattr(sock, "_inqueue"): self._sock._inqueue = ""
327:
328: # end __init__
329:
330: def __getattr__(self, key):
331: return getattr(self._sock, key)
332: # end __getattr__
333:
334: def close(self):
335: self._sock.close()
336: self._sock = None
337: # end close
338:
339: def write(self, data):
340: self.send(data)
341: # end write
342:
343: def read(self, size=-1):
344: _sock = self._sock
345: _bufsize = self._bufsize
346: while 1:
347: datalen = len(_sock._inqueue)
348: if datalen >= size >= 0:
349: break
350: bufsize = _bufsize
351: if size > 0:
352: bufsize = min(bufsize, size - datalen )
353: buf = self.recv(bufsize)
354: if not buf:
355: break
356: _sock._inqueue = _sock._inqueue + buf
357: data = _sock._inqueue
358: _sock._inqueue = ""
359: if size > 0 and datalen > size:
360: _sock._inqueue = data[size:]
361: data = data[:size]
362: return data
363: # end read
364:
365: def readline(self, size=-1):
366: _sock = self._sock
367: _bufsize = self._bufsize
368: while 1:
369: idx = string.find(_sock._inqueue, "\n")
370: if idx >= 0:
371: break
372: datalen = len(_sock._inqueue)
373: if datalen >= size >= 0:
374: break
375: bufsize = _bufsize
376: if size > 0:
377: bufsize = min(bufsize, size - datalen )
378: buf = self.recv(bufsize)
379: if not buf:
380: break
381: _sock._inqueue = _sock._inqueue + buf
382:
383: data = _sock._inqueue
384: _sock._inqueue = ""
385: if idx >= 0:
386: idx = idx + 1
387: _sock._inqueue = data[idx:]
388: data = data[:idx]
389: elif size > 0 and datalen > size:
390: _sock._inqueue = data[size:]
391: data = data[:size]
392: return data
393: # end readline
394:
395: def readlines(self, sizehint=-1):
396: result = []
397: data = self.read()
398: while data:
399: idx = string.find(data, "\n")
400: if idx >= 0:
401: idx = idx + 1
402: result.append( data[:idx] )
403: data = data[idx:]
404: else:
405: result.append( data )
406: data = ""
407: return result
408: # end readlines
409:
410: def flush(self): pass
411:
412: # end TimeoutFile
413:
414:
415: #
416: # Silently replace the socket() builtin function with
417: # our timeoutsocket() definition.
418: #
419: if not hasattr(socket, "_no_timeoutsocket"):
420: socket._no_timeoutsocket = socket.socket
421: socket.socket = timeoutsocket
422: del socket
423: socket = timeoutsocket
424: # Finis
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>