Commit a84d9d53 authored by Amos Latteier's avatar Amos Latteier

Bring medusa libraries up to date with current medusa stuff. This should...

Bring medusa libraries up to date with current medusa stuff. This should improve FTP Server on win95 problems, bogus DNS IP address problems, potential memory problems, and more. Also included in a little patch of my own to asyncore.py to fail on shutodown sockets in a slightly more friendly way.
parent c3526c9b
# -*- Mode: Python; tab-width: 4 -*-
# $Id: asynchat.py,v 1.7 1999/04/09 00:37:33 amos Exp $
# $Id: asynchat.py,v 1.8 1999/05/26 02:08:29 amos Exp $
# Author: Sam Rushing <rushing@nightmare.com>
# ======================================================================
......@@ -99,7 +99,6 @@ class async_chat (asyncore.dispatcher):
elif type(terminator) == type(0):
# numeric terminator
n = terminator
lb = lb
if lb < n:
self.collect_incoming_data (self.ac_in_buffer)
self.ac_in_buffer = ''
......@@ -159,7 +158,13 @@ class async_chat (asyncore.dispatcher):
def writable (self):
"predicate for inclusion in the writable for select()"
return len(self.ac_out_buffer) or len(self.producer_fifo) or (not self.connected)
# return len(self.ac_out_buffer) or len(self.producer_fifo) or (not self.connected)
# this is about twice as fast, though not as clear.
return not (
(self.ac_out_buffer is '') and
self.producer_fifo.is_empty() and
self.connected
)
def close_when_done (self):
"automatically close this channel once the outgoing queue is empty"
......@@ -242,6 +247,9 @@ class fifo:
def __len__ (self):
return len(self.list)
def is_empty (self):
return self.list == []
def first (self):
return self.list[0]
......
# -*- Mode: Python; tab-width: 4 -*-
# $Id: asyncore.py,v 1.2 1999/04/09 00:37:33 amos Exp $
# $Id: asyncore.py,v 1.3 1999/05/26 02:08:30 amos Exp $
# Author: Sam Rushing <rushing@nightmare.com>
# ======================================================================
......@@ -37,17 +37,20 @@ if os.name == 'nt':
EALREADY = 10037
ECONNRESET = 10054
ENOTCONN = 10057
ESHUTDOWN = 10058
else:
from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, ENOTCONN
from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, ENOTCONN, ESHUTDOWN
socket_map = {}
def poll (timeout=0.0):
if socket_map:
sockets = socket_map.keys()
r = filter (lambda x: x.readable(), sockets)
w = filter (lambda x: x.writable(), sockets)
e = []
r = []; w = []; e = []
for s in socket_map.keys():
if s.readable():
r.append (s)
if s.writable():
w.append (s)
(r,w,e) = select.select (r,w,e, timeout)
......@@ -140,12 +143,14 @@ class dispatcher:
return '<__repr__ (self) failed for object at %x (addr=%s)>' % (id(self),ar)
def add_channel (self):
self.log ('adding channel %s' % self)
if __debug__:
self.log ('adding channel %s' % self)
socket_map [self] = 1
def del_channel (self):
if socket_map.has_key (self):
self.log ('closing channel %d:%s' % (self.fileno(), self))
if __debug__:
self.log ('closing channel %d:%s' % (self.fileno(), self))
del socket_map [self]
def create_socket (self, family, type):
......@@ -155,7 +160,8 @@ class dispatcher:
self.add_channel()
def set_socket (self, socket):
self.socket = socket
# This is done so we can be called safely from __init__
self.__dict__['socket'] = socket
self.add_channel()
def set_reuse_addr (self):
......@@ -245,7 +251,7 @@ class dispatcher:
return data
except socket.error, why:
# winsock sometimes throws ENOTCONN
if why[0] in [ECONNRESET, ENOTCONN]:
if why[0] in [ECONNRESET, ENOTCONN, ESHUTDOWN]:
self.handle_close()
return ''
else:
......@@ -257,11 +263,9 @@ class dispatcher:
# cheap inheritance, used to pass all other attribute
# references to the underlying socket object.
# NOTE: this may be removed soon for performance reasons.
def __getattr__ (self, attr):
if attr != 'socket':
return getattr (self.socket, attr)
else:
raise AttributeError, attr
return getattr (self.socket, attr)
def log (self, message):
print 'log:', message
......@@ -310,22 +314,28 @@ class dispatcher:
self.close()
def handle_expt (self):
self.log ('unhandled exception')
if __debug__:
self.log ('unhandled exception')
def handle_read (self):
self.log ('unhandled read event')
if __debug__:
self.log ('unhandled read event')
def handle_write (self):
self.log ('unhandled write event')
if __debug__:
self.log ('unhandled write event')
def handle_connect (self):
self.log ('unhandled connect event')
if __debug__:
self.log ('unhandled connect event')
def handle_accept (self):
self.log ('unhandled accept event')
if __debug__:
self.log ('unhandled accept event')
def handle_close (self):
self.log ('unhandled close event')
if __debug__:
self.log ('unhandled close event')
self.close()
# ---------------------------------------------------------------------------
......
......@@ -8,7 +8,7 @@
# If you are interested in using this software in a commercial context,
# or in purchasing support, please contact the author.
RCS_ID = '$Id: ftp_server.py,v 1.4 1999/04/29 23:36:09 amos Exp $'
RCS_ID = '$Id: ftp_server.py,v 1.5 1999/05/26 02:08:30 amos Exp $'
# An extensible, configurable, asynchronous FTP server.
#
......@@ -54,9 +54,6 @@ import time
VERSION = string.split(RCS_ID)[2]
IP_ADDRESS = socket.gethostbyname (socket.gethostname())
HOSTNAME = socket.gethostbyaddr (IP_ADDRESS)[0]
from counter import counter
import producers
import status_handler
......@@ -286,7 +283,7 @@ class ftp_channel (asynchat.async_chat):
if pa:
if pa.ready:
# a connection has already been made.
conn, addr = self.passive_acceptor.ready
conn, addr = pa.ready
cdc = recv_channel (self, addr, fd)
cdc.set_socket (conn)
cdc.connected = 1
......@@ -698,14 +695,19 @@ class ftp_server (asyncore.dispatcher):
def __init__ (
self,
authorizer,
hostname =HOSTNAME,
hostname =None,
port =21,
resolver =None,
logger_object=logger.file_logger (sys.stdout)
):
self.port = port
self.authorizer = authorizer
self.hostname = hostname
if hostname is None:
self.hostname = socket.gethostname()
else:
self.hostname = hostname
# statistics
self.total_sessions = counter()
self.closed_sessions = counter()
......@@ -1053,8 +1055,7 @@ if os.name == 'posix':
import sys
fs = ftp_server (
unix_authorizer(),
HOSTNAME,
string.atoi (port)
port=string.atoi (port)
)
try:
asyncore.loop()
......
......@@ -9,7 +9,7 @@
# interested in using this software in a commercial context, or in
# purchasing support, please contact the author.
RCS_ID = '$Id: http_server.py,v 1.6 1999/04/09 00:37:33 amos Exp $'
RCS_ID = '$Id: http_server.py,v 1.7 1999/05/26 02:08:30 amos Exp $'
# python modules
import os
......@@ -215,6 +215,12 @@ class http_request:
wrap_in_chunking = 1
else:
close_it = 1
elif self.version is None:
# Although we don't *really* support http/0.9 (because we'd have to
# use \r\n as a terminator, and it would just yuck up a lot of stuff)
# it's very common for developers to not want to type a version number
# when using telnet to debug a server.
close_it = 1
outgoing_header = producers.simple_producer (self.build_reply_header())
......@@ -388,9 +394,25 @@ class http_channel (asynchat.async_chat):
return result
def recv (self, buffer_size):
result = asynchat.async_chat.recv (self, buffer_size)
self.server.bytes_in.increment (len(result))
return result
try:
result = asynchat.async_chat.recv (self, buffer_size)
self.server.bytes_in.increment (len(result))
return result
except MemoryError:
# --- Save a Trip to Your Service Provider ---
# It's possible for a process to eat up all the memory of
# the machine, and put it in an extremely wedged state,
# where medusa keeps running and can't be shut down. This
# is where MemoryError tends to get thrown, though of
# course it could get thrown elsewhere.
sys.exit ("Out of Memory!")
def handle_error (self):
t, v = sys.exc_info()[:2]
if t is SystemExit:
raise t, v
else:
asynchat.async_chat.handle_error (self)
def log (self, *args):
pass
......
......@@ -4,7 +4,8 @@
# Author: Sam Rushing <rushing@nightmare.com>
#
RCS_ID = '$Id: resolver.py,v 1.2 1999/04/09 00:37:33 amos Exp $'
RCS_ID = '$Id: resolver.py,v 1.3 1999/05/26 02:08:30 amos Exp $'
# Fast, low-overhead asynchronous name resolver. uses 'pre-cooked'
# DNS requests, unpacks only as much as it needs of the reply.
......@@ -12,6 +13,11 @@ RCS_ID = '$Id: resolver.py,v 1.2 1999/04/09 00:37:33 amos Exp $'
# see rfc1035 for details
import string
import asyncore
import socket
import sys
import time
from counter import counter
VERSION = string.split(RCS_ID)[2]
......@@ -177,11 +183,6 @@ def unpack_ptr_reply (r):
return 0, None
from counter import counter
import asyncore
import socket
import sys
# This is a UDP (datagram) resolver.
#
......@@ -202,6 +203,7 @@ class resolver (asyncore.dispatcher):
self.create_socket (socket.AF_INET, socket.SOCK_DGRAM)
self.server = server
self.request_map = {}
self.last_reap_time = int(time.time()) # reap every few minutes
def writable (self):
return 0
......@@ -213,18 +215,38 @@ class resolver (asyncore.dispatcher):
print 'closing!'
self.close()
def handle_error (self): # don't close the connection on error
(file,fun,line), t, v, tbinfo = asyncore.compact_traceback()
print 'Problem with DNS lookup (%s:%s %s)' % (t, v, tbinfo)
def get_id (self):
return (self.id.as_long() % (1<<16))
def reap (self): # find DNS requests that have timed out
now = int(time.time())
if now - self.last_reap_time > 180: # reap every 3 minutes
self.last_reap_time = now # update before we forget
for k,(host,unpack,callback,when) in self.request_map.items():
if now - when > 180: # over 3 minutes old
del self.request_map[k]
try: # same code as in handle_read
callback (host, 0, None) # timeout val is (0,None)
except:
(file,fun,line), t, v, tbinfo = asyncore.compact_traceback()
print t,v,tbinfo
def resolve (self, host, callback):
self.reap() # first, get rid of old guys
self.socket.sendto (
fast_address_request (host, self.get_id()),
(self.server, 53)
)
self.request_map [self.get_id()] = host, unpack_address_reply, callback
self.request_map [self.get_id()] = (
host, unpack_address_reply, callback, int(time.time()))
self.id.increment()
def resolve_ptr (self, host, callback):
self.reap() # first, get rid of old guys
ip = string.split (host, '.')
ip.reverse()
ip = string.join (ip, '.') + '.in-addr.arpa'
......@@ -232,7 +254,8 @@ class resolver (asyncore.dispatcher):
fast_ptr_request (ip, self.get_id()),
(self.server, 53)
)
self.request_map [self.get_id()] = host, unpack_ptr_reply, callback
self.request_map [self.get_id()] = (
host, unpack_ptr_reply, callback, int(time.time()))
self.id.increment()
def handle_read (self):
......@@ -241,7 +264,7 @@ class resolver (asyncore.dispatcher):
# that <whence> is the server we sent the request to.
id = (ord(reply[0])<<8) + ord(reply[1])
if self.request_map.has_key (id):
host, unpack, callback = self.request_map[id]
host, unpack, callback, when = self.request_map[id]
del self.request_map[id]
ttl, answer = unpack (reply)
try:
......@@ -270,7 +293,6 @@ class rbl (resolver):
print repr(r)
return 0, rcode # (ttl, answer)
import time
class hooked_callback:
def __init__ (self, hook, callback):
......
# -*- Mode: Python; tab-width: 4 -*-
# $Id: asynchat.py,v 1.7 1999/04/09 00:37:33 amos Exp $
# $Id: asynchat.py,v 1.8 1999/05/26 02:08:29 amos Exp $
# Author: Sam Rushing <rushing@nightmare.com>
# ======================================================================
......@@ -99,7 +99,6 @@ class async_chat (asyncore.dispatcher):
elif type(terminator) == type(0):
# numeric terminator
n = terminator
lb = lb
if lb < n:
self.collect_incoming_data (self.ac_in_buffer)
self.ac_in_buffer = ''
......@@ -159,7 +158,13 @@ class async_chat (asyncore.dispatcher):
def writable (self):
"predicate for inclusion in the writable for select()"
return len(self.ac_out_buffer) or len(self.producer_fifo) or (not self.connected)
# return len(self.ac_out_buffer) or len(self.producer_fifo) or (not self.connected)
# this is about twice as fast, though not as clear.
return not (
(self.ac_out_buffer is '') and
self.producer_fifo.is_empty() and
self.connected
)
def close_when_done (self):
"automatically close this channel once the outgoing queue is empty"
......@@ -242,6 +247,9 @@ class fifo:
def __len__ (self):
return len(self.list)
def is_empty (self):
return self.list == []
def first (self):
return self.list[0]
......
# -*- Mode: Python; tab-width: 4 -*-
# $Id: asyncore.py,v 1.2 1999/04/09 00:37:33 amos Exp $
# $Id: asyncore.py,v 1.3 1999/05/26 02:08:30 amos Exp $
# Author: Sam Rushing <rushing@nightmare.com>
# ======================================================================
......@@ -37,17 +37,20 @@ if os.name == 'nt':
EALREADY = 10037
ECONNRESET = 10054
ENOTCONN = 10057
ESHUTDOWN = 10058
else:
from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, ENOTCONN
from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, ENOTCONN, ESHUTDOWN
socket_map = {}
def poll (timeout=0.0):
if socket_map:
sockets = socket_map.keys()
r = filter (lambda x: x.readable(), sockets)
w = filter (lambda x: x.writable(), sockets)
e = []
r = []; w = []; e = []
for s in socket_map.keys():
if s.readable():
r.append (s)
if s.writable():
w.append (s)
(r,w,e) = select.select (r,w,e, timeout)
......@@ -140,12 +143,14 @@ class dispatcher:
return '<__repr__ (self) failed for object at %x (addr=%s)>' % (id(self),ar)
def add_channel (self):
self.log ('adding channel %s' % self)
if __debug__:
self.log ('adding channel %s' % self)
socket_map [self] = 1
def del_channel (self):
if socket_map.has_key (self):
self.log ('closing channel %d:%s' % (self.fileno(), self))
if __debug__:
self.log ('closing channel %d:%s' % (self.fileno(), self))
del socket_map [self]
def create_socket (self, family, type):
......@@ -155,7 +160,8 @@ class dispatcher:
self.add_channel()
def set_socket (self, socket):
self.socket = socket
# This is done so we can be called safely from __init__
self.__dict__['socket'] = socket
self.add_channel()
def set_reuse_addr (self):
......@@ -245,7 +251,7 @@ class dispatcher:
return data
except socket.error, why:
# winsock sometimes throws ENOTCONN
if why[0] in [ECONNRESET, ENOTCONN]:
if why[0] in [ECONNRESET, ENOTCONN, ESHUTDOWN]:
self.handle_close()
return ''
else:
......@@ -257,11 +263,9 @@ class dispatcher:
# cheap inheritance, used to pass all other attribute
# references to the underlying socket object.
# NOTE: this may be removed soon for performance reasons.
def __getattr__ (self, attr):
if attr != 'socket':
return getattr (self.socket, attr)
else:
raise AttributeError, attr
return getattr (self.socket, attr)
def log (self, message):
print 'log:', message
......@@ -310,22 +314,28 @@ class dispatcher:
self.close()
def handle_expt (self):
self.log ('unhandled exception')
if __debug__:
self.log ('unhandled exception')
def handle_read (self):
self.log ('unhandled read event')
if __debug__:
self.log ('unhandled read event')
def handle_write (self):
self.log ('unhandled write event')
if __debug__:
self.log ('unhandled write event')
def handle_connect (self):
self.log ('unhandled connect event')
if __debug__:
self.log ('unhandled connect event')
def handle_accept (self):
self.log ('unhandled accept event')
if __debug__:
self.log ('unhandled accept event')
def handle_close (self):
self.log ('unhandled close event')
if __debug__:
self.log ('unhandled close event')
self.close()
# ---------------------------------------------------------------------------
......
......@@ -8,7 +8,7 @@
# If you are interested in using this software in a commercial context,
# or in purchasing support, please contact the author.
RCS_ID = '$Id: ftp_server.py,v 1.4 1999/04/29 23:36:09 amos Exp $'
RCS_ID = '$Id: ftp_server.py,v 1.5 1999/05/26 02:08:30 amos Exp $'
# An extensible, configurable, asynchronous FTP server.
#
......@@ -54,9 +54,6 @@ import time
VERSION = string.split(RCS_ID)[2]
IP_ADDRESS = socket.gethostbyname (socket.gethostname())
HOSTNAME = socket.gethostbyaddr (IP_ADDRESS)[0]
from counter import counter
import producers
import status_handler
......@@ -286,7 +283,7 @@ class ftp_channel (asynchat.async_chat):
if pa:
if pa.ready:
# a connection has already been made.
conn, addr = self.passive_acceptor.ready
conn, addr = pa.ready
cdc = recv_channel (self, addr, fd)
cdc.set_socket (conn)
cdc.connected = 1
......@@ -698,14 +695,19 @@ class ftp_server (asyncore.dispatcher):
def __init__ (
self,
authorizer,
hostname =HOSTNAME,
hostname =None,
port =21,
resolver =None,
logger_object=logger.file_logger (sys.stdout)
):
self.port = port
self.authorizer = authorizer
self.hostname = hostname
if hostname is None:
self.hostname = socket.gethostname()
else:
self.hostname = hostname
# statistics
self.total_sessions = counter()
self.closed_sessions = counter()
......@@ -1053,8 +1055,7 @@ if os.name == 'posix':
import sys
fs = ftp_server (
unix_authorizer(),
HOSTNAME,
string.atoi (port)
port=string.atoi (port)
)
try:
asyncore.loop()
......
......@@ -9,7 +9,7 @@
# interested in using this software in a commercial context, or in
# purchasing support, please contact the author.
RCS_ID = '$Id: http_server.py,v 1.6 1999/04/09 00:37:33 amos Exp $'
RCS_ID = '$Id: http_server.py,v 1.7 1999/05/26 02:08:30 amos Exp $'
# python modules
import os
......@@ -215,6 +215,12 @@ class http_request:
wrap_in_chunking = 1
else:
close_it = 1
elif self.version is None:
# Although we don't *really* support http/0.9 (because we'd have to
# use \r\n as a terminator, and it would just yuck up a lot of stuff)
# it's very common for developers to not want to type a version number
# when using telnet to debug a server.
close_it = 1
outgoing_header = producers.simple_producer (self.build_reply_header())
......@@ -388,9 +394,25 @@ class http_channel (asynchat.async_chat):
return result
def recv (self, buffer_size):
result = asynchat.async_chat.recv (self, buffer_size)
self.server.bytes_in.increment (len(result))
return result
try:
result = asynchat.async_chat.recv (self, buffer_size)
self.server.bytes_in.increment (len(result))
return result
except MemoryError:
# --- Save a Trip to Your Service Provider ---
# It's possible for a process to eat up all the memory of
# the machine, and put it in an extremely wedged state,
# where medusa keeps running and can't be shut down. This
# is where MemoryError tends to get thrown, though of
# course it could get thrown elsewhere.
sys.exit ("Out of Memory!")
def handle_error (self):
t, v = sys.exc_info()[:2]
if t is SystemExit:
raise t, v
else:
asynchat.async_chat.handle_error (self)
def log (self, *args):
pass
......
......@@ -4,7 +4,8 @@
# Author: Sam Rushing <rushing@nightmare.com>
#
RCS_ID = '$Id: resolver.py,v 1.2 1999/04/09 00:37:33 amos Exp $'
RCS_ID = '$Id: resolver.py,v 1.3 1999/05/26 02:08:30 amos Exp $'
# Fast, low-overhead asynchronous name resolver. uses 'pre-cooked'
# DNS requests, unpacks only as much as it needs of the reply.
......@@ -12,6 +13,11 @@ RCS_ID = '$Id: resolver.py,v 1.2 1999/04/09 00:37:33 amos Exp $'
# see rfc1035 for details
import string
import asyncore
import socket
import sys
import time
from counter import counter
VERSION = string.split(RCS_ID)[2]
......@@ -177,11 +183,6 @@ def unpack_ptr_reply (r):
return 0, None
from counter import counter
import asyncore
import socket
import sys
# This is a UDP (datagram) resolver.
#
......@@ -202,6 +203,7 @@ class resolver (asyncore.dispatcher):
self.create_socket (socket.AF_INET, socket.SOCK_DGRAM)
self.server = server
self.request_map = {}
self.last_reap_time = int(time.time()) # reap every few minutes
def writable (self):
return 0
......@@ -213,18 +215,38 @@ class resolver (asyncore.dispatcher):
print 'closing!'
self.close()
def handle_error (self): # don't close the connection on error
(file,fun,line), t, v, tbinfo = asyncore.compact_traceback()
print 'Problem with DNS lookup (%s:%s %s)' % (t, v, tbinfo)
def get_id (self):
return (self.id.as_long() % (1<<16))
def reap (self): # find DNS requests that have timed out
now = int(time.time())
if now - self.last_reap_time > 180: # reap every 3 minutes
self.last_reap_time = now # update before we forget
for k,(host,unpack,callback,when) in self.request_map.items():
if now - when > 180: # over 3 minutes old
del self.request_map[k]
try: # same code as in handle_read
callback (host, 0, None) # timeout val is (0,None)
except:
(file,fun,line), t, v, tbinfo = asyncore.compact_traceback()
print t,v,tbinfo
def resolve (self, host, callback):
self.reap() # first, get rid of old guys
self.socket.sendto (
fast_address_request (host, self.get_id()),
(self.server, 53)
)
self.request_map [self.get_id()] = host, unpack_address_reply, callback
self.request_map [self.get_id()] = (
host, unpack_address_reply, callback, int(time.time()))
self.id.increment()
def resolve_ptr (self, host, callback):
self.reap() # first, get rid of old guys
ip = string.split (host, '.')
ip.reverse()
ip = string.join (ip, '.') + '.in-addr.arpa'
......@@ -232,7 +254,8 @@ class resolver (asyncore.dispatcher):
fast_ptr_request (ip, self.get_id()),
(self.server, 53)
)
self.request_map [self.get_id()] = host, unpack_ptr_reply, callback
self.request_map [self.get_id()] = (
host, unpack_ptr_reply, callback, int(time.time()))
self.id.increment()
def handle_read (self):
......@@ -241,7 +264,7 @@ class resolver (asyncore.dispatcher):
# that <whence> is the server we sent the request to.
id = (ord(reply[0])<<8) + ord(reply[1])
if self.request_map.has_key (id):
host, unpack, callback = self.request_map[id]
host, unpack, callback, when = self.request_map[id]
del self.request_map[id]
ttl, answer = unpack (reply)
try:
......@@ -270,7 +293,6 @@ class rbl (resolver):
print repr(r)
return 0, rcode # (ttl, answer)
import time
class hooked_callback:
def __init__ (self, hook, callback):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment