BoboMailHTTPD.py
上传用户:gyjinxi
上传日期:2007-01-04
资源大小:159k
文件大小:18k
- #!/usr/bin/env python
- ##############################################################################
- #
- # Zope Public License (ZPL) Version 0.9.5
- # ---------------------------------------
- #
- # Copyright (c) Digital Creations. All rights reserved.
- #
- # Redistribution and use in source and binary forms, with or without
- # modification, are permitted provided that the following conditions are
- # met:
- #
- # 1. Redistributions in source code must retain the above copyright
- # notice, this list of conditions, and the following disclaimer.
- #
- # 2. Redistributions in binary form must reproduce the above copyright
- # notice, this list of conditions, and the following disclaimer in
- # the documentation and/or other materials provided with the
- # distribution.
- #
- # 3. Any use, including use of the Zope software to operate a website,
- # must either comply with the terms described below under
- # "Attribution" or alternatively secure a separate license from
- # Digital Creations. Digital Creations will not unreasonably
- # deny such a separate license in the event that the request
- # explains in detail a valid reason for withholding attribution.
- #
- # 4. All advertising materials and documentation mentioning
- # features derived from or use of this software must display
- # the following acknowledgement:
- #
- # "This product includes software developed by Digital Creations
- # for use in the Z Object Publishing Environment
- # (http://www.zope.org/)."
- #
- # In the event that the product being advertised includes an
- # intact Zope distribution (with copyright and license included)
- # then this clause is waived.
- #
- # 5. Names associated with Zope or Digital Creations must not be used to
- # endorse or promote products derived from this software without
- # prior written permission from Digital Creations.
- #
- # 6. Modified redistributions of any form whatsoever must retain
- # the following acknowledgment:
- #
- # "This product includes software developed by Digital Creations
- # for use in the Z Object Publishing Environment
- # (http://www.zope.org/)."
- #
- # Intact (re-)distributions of any official Zope release do not
- # require an external acknowledgement.
- #
- # 7. Modifications are encouraged but must be packaged separately as
- # patches to official Zope releases. Distributions that do not
- # clearly separate the patches from the original work must be clearly
- # labeled as unofficial distributions. Modifications which do not
- # carry the name Zope may be packaged in any form, as long as they
- # conform to all of the clauses above.
- #
- #
- # Disclaimer
- #
- # THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
- # EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
- # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- # OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- # SUCH DAMAGE.
- #
- # Attribution
- #
- # Individuals or organizations using this software as a web site must
- # provide attribution by placing the accompanying "button" and a link
- # to the accompanying "credits page" on the website's main entry
- # point. In cases where this placement of attribution is not
- # feasible, a separate arrangment must be concluded with Digital
- # Creations. Those using the software for purposes other than web
- # sites must provide a corresponding attribution in locations that
- # include a copyright using a manner best suited to the application
- # environment. Where attribution is not possible, or is considered
- # to be onerous for some other reason, a request should be made to
- # Digital Creations to waive this requirement in writing. As stated
- # above, for valid requests, Digital Creations will not unreasonably
- # deny such requests.
- #
- # This software consists of contributions made by Digital Creations and
- # many individuals on behalf of Digital Creations. Specific
- # attributions are listed in the accompanying credits file.
- #
- ##############################################################################
- """Simple Bobo HTTP Server (based on CGIHTTPServer.py)
- What's Bobo?
- An open source collection of tools for developing
- world-wide web applications in Python. Find out more at
- http://www.digicool.com/releases/bobo/
- What's Zope?
- Zope is the result of the merging of Bobo and Principia.
- For more info visit the Zope web site at
- http://www.zope.org/
- What does ZopeHTTPServer.py do?
- It is a very simple web server that published a module
- with Bobo. ZopeHTTPServer.py is probably the easiest way
- to publish a module with Bobo. It does not require anything
- besides Python and Bobo to be installed. It is fast because
- it publishes a module as a long running process.
- Why not use CGI or PCGI or Medusa?
- ZopeHTTPServer is much simpler to use than other Bobo publishers.
- ZopeHTTPServer does not require any configuration. It has a
- friendly license. It also offers excellent performance, threaded
- publishing, and streaming response, not to mention all the Bobo
- basics like authenication, control of response content-type,
- file uploads, etc.
- Features:
- -Only publishes one module at a time
- -Cannot reload a module
- -Does not serve files or CGI programs, only Bobo
- -Single-threaded or multi-threaded publishing
- -Unbuffered output so response can stream
- -Works under Python 1.5 and Python 1.4 (mostly)
- -At least it's easy to use
- Basic Useage:
- ZopeHTTPServer.py <path to module>
- This publishes your module on the web.
- Usage:
- ZopeHTTPServer.py [-p <port>] [-t] [-h <host address>] <path to module> [var=value,]
- Starts a web server which publishes the specified module.
- -p <port>: Run the server on the specified port. The default
- port is 9673.
- -t: Use a multi-threaded HTTP server. The default is a
- single-threaded server. Note, your platform must support
- threads to use the multi-threaded server.
- -h: Specifies the host address. Python normally supplies this by default.
- -P <pid file>: Specifies a file in which the server will write its PID
- -s <script_name>: Specify a virtual script name.
- Specifying additional arguments of the form var=value sets environment
- variables. You can use this facility to set things like BOBO_DEBUG_MODE=1
- and the like. These setting override default environment settings, so you
- can do things like change the SCRIPT_NAME or other weird stuff. NOTE:
- var=value settings can come before or after the path to module.
- You can also use the server from Python like this::
- import ZopeHTTPServer
- ZopeHTTPServer.main(<args>)
- The form the args take is exactly the same as the command line arguments.
- For example::
- ZopeHTTPServer.main("-t","-p 80","/home/amos/Test.py","BOBO_DEBUG_MODE=1")
- Using ZopeHTTPServer behind Apache
- ZopeHTTPServer doesn't have many fancy features, but it's easy to
- use. Andreas Kostyrka suggests using ZopeHTTPServer with an Apache
- proxy to get some of Apache's cool features like SSL.
-
- Run ZopeHTTPServer on a high port and an IP address which is behind
- a firewall. For example 127.0.0.2, port 5000::
-
- ZopeHTTPServer -p 5000 -h 127.0.0.2 /home/amos/MyModule.py SCRIPT_NAME=/intern
-
- Then use Apache's proxy module to map requests to ZopeHTTPServer::
-
- RewriteEngine on
- RewriteRule ^/intern/(.*) http://127.0.0.2:5000/$1 [P]
- This rule maps request beginning with '/intern/' to ZopeHTTPServer.
- Notice that we set the ZopeHTTPServer SCRIPT_NAME to '/intern' so that
- it knows how it's being accessed. You can change other aspects of
- ZopeHTTPServer's environment to match the proxying environment. For example
- if you are using SSL, you should set HTTPS=on.
- Known bugs:
- PUT doesn't work very well at all.
-
- REQUEST.write never closes the connection under Python 1.4
- There is a problem with mutipart/form-data POST requests
- under win32 and python 1.5.2a2. I think the problem is
- with cgi.py
- Under win32 interupted HTTP requests can raise exceptions,
- but they don't seem to cause any problems.
- """
- __version__='$Revision: 1.15 $'[11:-2]
- import SocketServer
- import SimpleHTTPServer
- import BaseHTTPServer
- import os
- import sys
- import urllib
- import string
- import tempfile
- import socket
- try: from cStringIO import StringIO
- except ImportError: from StringIO import StringIO
- class ResponseWriter:
- """Logs response and reorders it so status header is
- first. After status header has been written, the rest
- of the response can stream."""
-
- def __init__(self,handler):
- self.handler=handler
- self.data=""
- self.latch=None
-
- def write(self,data):
- if self.latch:
- self.handler.wfile.write(data)
- else:
- self.data=self.data+data
- start=string.find(self.data,"Status: ")
- if start != -1:
- end=string.find(self.data,"n",start)
- status=self.data[start+8:end]
- code, message=tuple(string.split(status," ",1))
- self.handler.send_response(string.atoi(code),message)
- self.handler.wfile.write(self.data[:start]+
- self.data[end+1:])
- self.latch=1
- def flush(self):
- pass
-
- class BoboRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
- """Handler for GET, HEAD and POST. Only publishes one
- Bobo module at a time. All URLs go to the published module.
- Does not serve files or CGI programs.
- """
-
- server_version = "ZopeHTTP/" + __version__
- buffer_size = 8192
- env_override={}
- script=''
- def setup(self):
- """overrides defaults to set larger buffer for rfile
- otherwise large POST requests seem to take forever."""
- self.connection = self.request
- self.rfile = self.connection.makefile('rb', self.buffer_size)
- self.wfile = self.connection.makefile('wb', 0)
- def hack_si(self):
- #cgi.py doesn't like to parse file uploads unless we do this
- si_len=string.atoi(self.headers.getheader('content-length'))
- if si_len < 1048576:
- si=StringIO()
- si.write(self.rfile.read(si_len))
- else:
- bufsize=self.buffer_size
- si=tempfile.TemporaryFile()
- while si_len > 0:
- if si_len < bufsize:
- bufsize=si_len
- data=self.rfile.read(bufsize)
- si_len=si_len - len(data)
- si.write(data)
- si.seek(0)
- self.rfile=si
- def do_POST(self):
- if string.find(self.headers.getheader('content-type'),
- 'multipart/form-data') != -1:
- self.hack_si()
- self.publish_module()
- def do_GET(self):
- self.publish_module()
- def do_HEAD(self):
- #is this necessary?
- self.publish_module()
-
- def do_PUT(self):
- #doesn't work very well yet
- self.env_override['REQUEST_METHOD']='PUT'
- self.env_override['CONTENT_TYPE']='application/data'
- self.hack_si()
- self.publish_module()
- def publish_module(self):
- publish_module(
- self.module_name,
- stdin=self.rfile,
- stdout=ResponseWriter(self),
- stderr=sys.stderr,
- environ=self.get_environment())
- def get_environment(self):
- #Partially derived from CGIHTTPServer
- env={}
- env['SERVER_SOFTWARE'] = self.version_string()
- env['SERVER_NAME'] = self.server.server_name
- env['SERVER_PORT'] = str(self.server.server_port)
- env['SERVER_PROTOCOL'] = self.protocol_version
- env['GATEWAY_INTERFACE'] = 'CGI/1.1'
- rest = self.path
- i = string.rfind(rest, '?')
- if i >= 0:
- rest, query = rest[:i], rest[i+1:]
- else:
- query = ''
- uqrest = urllib.unquote(rest)
- script=self.script
- if script:
- env['SCRIPT_NAME'] = script[:-1]
- if uqrest[:len(script)]==script:
- uqrest=uqrest[len(script)-1:]
- else: env['SCRIPT_NAME'] = ''
- env['PATH_INFO'] = uqrest
- env['REQUEST_METHOD'] = self.command
- env['PATH_TRANSLATED'] = self.translate_path(uqrest)
- if query:
- env['QUERY_STRING'] = query
- host = self.address_string()
- if host != self.client_address[0]:
- env['REMOTE_HOST'] = host
- env['REMOTE_ADDR'] = self.client_address[0]
- env['CONTENT_TYPE'] = self.headers.getheader('content-type')
- length = self.headers.getheader('content-length')
- if length:
- env['CONTENT_LENGTH'] = length
- # handle the rest of the environment
- for k,v in self.headers.items():
- k=string.upper(string.join(string.split(k,"-"),"_"))
- if not env.has_key(k) and v:
- env['HTTP_'+k]=v
- if self.env_override:
- for k,v in self.env_override.items():
- env[k]=v
- return env
- class NonThreadingHTTPServer(BaseHTTPServer.HTTPServer):
- "The normal HTTPServer with some socket tweaks"
-
- def server_bind(self):
- # Modified version of server_bind that allows unconnected
- # win32 boxes to run the server without errors.
- self.socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
- SocketServer.TCPServer.server_bind(self)
- host, port = self.socket.getsockname()
- hostname=host
- if not host or host == '0.0.0.0':
- host = socket.gethostname()
- try:
- hostname, hostnames, hostaddrs = socket.gethostbyaddr(host)
- if '.' not in hostname:
- for host in hostnames:
- if '.' in host:
- hostname = host
- break
- except:
- pass
- self.server_name = hostname
- self.server_port = port
- def handle_request(self):
- """Handle one request, possibly blocking."""
- request, client_address = self.get_request()
- if self.verify_request(request, client_address):
- try:
- self.process_request(request, client_address)
- except SystemExit:
- self.handle_error(request, client_address)
- sys.exit(0)
- except:
- self.handle_error(request, client_address)
-
- class ThreadingHTTPServer(SocketServer.ThreadingMixIn,
- NonThreadingHTTPServer):
- "A threading HTTPServer with some socket tweaks"
- pass
- def try_to_become_nobody():
- # from CGIHTTPServer
- try: import pwd
- except: return
- try:
- nobody = pwd.getpwnam('nobody')[2]
- except pwd.error:
- nobody = 1 + max(map(lambda x: x[2], pwd.getpwall()))
- try: os.setuid(nobody)
- except os.error: pass
- def set_published_module(file,klass,env=None):
- dir,file=os.path.split(file)
- name,ext=os.path.splitext(file)
- klass.module_name=name
- klass.module_dir=dir
- cdir=os.path.join(dir,'Components')
- sys.path[0:0]=[dir,cdir,os.path.join(cdir,sys.platform)]
-
- if env is not None:
- klass.env_override=env
-
- global publish_module
- try:
- import ZPublisher
- publish_module=ZPublisher.publish_module
- except:
- import cgi_module_publisher
- publish_module=cgi_module_publisher.publish_module
-
- __import__(name) # to catch problem modules right away
- print "Publishing module %s" % name
- def start(module_file, host='', port=8080, threading=None,env=None):
- set_published_module(module_file,BoboRequestHandler,env)
- server_address = (host, port)
- if threading:
- try:
- import thread
- httpd = ThreadingHTTPServer(server_address,
- BoboRequestHandler)
- print "Using threading server"
- except ImportError:
- httpd = NonThreadingHTTPServer(server_address,
- BoboRequestHandler)
- else:
- httpd = NonThreadingHTTPServer(server_address,
- BoboRequestHandler)
- print "Serving HTTP on port", port, "..."
- try_to_become_nobody()
- try:
- httpd.serve_forever()
- except:
- httpd.socket.close()
- sys.exit(1)
- def die(message=''):
- if message: print 'nError: %sn' % message
- print __doc__
- sys.exit(1)
- def main(args=None):
- args=args or sys.argv[1:]
- import getopt
- optlist, args=getopt.getopt(args,"tp:h:P:s:")
- if len(args) < 1: die()
- env={}
- module_file=''
- for a in args:
- a=string.split(a,'=')
- if len(a)==1:
- if module_file: die('Unrecognized argument: %s' % a[0])
- module_file=a[0]
- elif len(a)==0: continue
- else:
- k, v = a[0], string.join(a[1:],'=')
- os.environ[k]=v
- env[k]=v
- port=9673
- threading=None
- host=''
- for k,v in optlist:
- if k=="-p":
- port=string.atoi(v)
- elif k=="-t":
- threading=1
- elif k=="-h":
- host=v
- elif k=="-P":
- open(v,'w').write(str(os.getpid()))
- elif k=='-s':
- while v[:1]=='/': v=v[1:]
- while v[-1:]=='/': v=v[:-1]
- BoboRequestHandler.script="/%s/" % v
- start(module_file,host,port,threading,env)
- if __name__=="__main__":
- if os.environ.has_key("QUERY_STRING"):
- print "Content-type: text/plainn"
- sys.path.insert(0, os.path.dirname(sys.argv[0]))
- import bobomailrc
- sys.path.insert(0, bobomailrc.app_dir)
-
- bobomailrc.image_dir = "GetFile?images"
- if "-d" in sys.argv:
- sys.stdout = sys.stderr = open(bobomailrc.access_log, "a")
- sys.argv.remove("-d")
-
- #remove "-t" if your Python-interpreter doesn't support threads
- main(["-t", "-p", str(bobomailrc.httpd_port), "main"])