Annotation of ECHO_content/timeoutsocket.py, revision 1.1

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: 
        !            88: __version__ = "$Revision: 1.23 $"
        !            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: #
        !           103: # Set up constants to test for Connected and Blocking operations.
        !           104: # We delete 'os' and 'errno' to keep our namespace clean(er).
        !           105: # Thanks to Alex Martelli and G. Li for the Windows error codes.
        !           106: #
        !           107: import os
        !           108: if os.name == "nt":
        !           109:     _IsConnected = ( 10022, 10056 )
        !           110:     _ConnectBusy = ( 10035, )
        !           111:     _AcceptBusy  = ( 10035, )
        !           112: else:
        !           113:     import errno
        !           114:     _IsConnected = ( errno.EISCONN, )
        !           115:     _ConnectBusy = ( errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK )
        !           116:     _AcceptBusy  = ( errno.EAGAIN, errno.EWOULDBLOCK )
        !           117:     del errno
        !           118: del os
        !           119: 
        !           120: 
        !           121: #
        !           122: # Default timeout value for ALL TimeoutSockets
        !           123: #
        !           124: _DefaultTimeout = None
        !           125: def setDefaultSocketTimeout(timeout):
        !           126:     global _DefaultTimeout
        !           127:     _DefaultTimeout = timeout
        !           128: def getDefaultSocketTimeout():
        !           129:     return _DefaultTimeout
        !           130: 
        !           131: #
        !           132: # Exceptions for socket errors and timeouts
        !           133: #
        !           134: Error = socket.error
        !           135: class Timeout(Exception):
        !           136:     pass
        !           137: 
        !           138: 
        !           139: #
        !           140: # Factory function
        !           141: #
        !           142: from socket import AF_INET, SOCK_STREAM
        !           143: def timeoutsocket(family=AF_INET, type=SOCK_STREAM, proto=None):
        !           144:     if family != AF_INET or type != SOCK_STREAM:
        !           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>