server.py :  » Wiki » Cloud-Wiki » cloudwiki-2.1 » cloud_wiki » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Wiki » Cloud Wiki 
Cloud Wiki » cloudwiki 2.1 » cloud_wiki » server.py
# Copyright (C) 2004 Scott W. Dunlop <sdunlop at users.sourceforge.net>
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

import os, sys
from traceback import format_exception,print_exception
from BaseHTTPServer import HTTPServer,BaseHTTPRequestHandler
from urllib import quote_plus
from urllib import unquote_plus
from exceptions import AttributeError,ValueError
from cStringIO import StringIO
from types import StringType

from html import normalize
from errors import *

from Cookie import SimpleCookie

class Request( object ):
    """
    An abstract base class containing the necessary code for parsing URL 
    requests that would normally be encountered by Cloud Wiki servers, and
    tracks the information relevant to the request.
    
    ! Instance Properties:
        
    * server -- refers to the server of origin for this request.
    * query -- contains the portion of the URI that describes the gateway or node referenced by the request.
    * action -- contains the portion of the URI that specifies the method of interaction with the node specified by
    the query attribute.  Not applicable for gateways.
    * args -- contains a dictionary of key:value argument pairs that followed the ? portion of the request URI.
    * user -- the user, as determined by the server.
    * cookies -- a SimpleCookie instance constructed from the http request.
    """
    
    server_version = "CloudWiki/2.0"
    __slots__ = 'query', 'action', 'args', 'user', 'cookies'
    
    def __init__( self ):
        self.query = None
        self.action = None
        self.args = {}
        self.user = None
        self.cookies = SimpleCookie()
        
    def getQuery( self ):
        return self.query
    def setQuery( self, query ):
        self.query = query
        return self
        
    def getAction( self ):
        return self.action
    def setAction( self, action ):
        self.action = action
        return self
        
    def getArgs( self ):
        return self.args
    def getArg( self, key, default=None ):
        return self.args.get(key, default)
    def setArgs( self, content ):
        self.args = args
        return self
    def setArg( self, key, value ):
        self.args[key] = value
        return self
        
    def getUser( self ):
        return self.user
    def setUser( self, user ):
        self.user = user
        return self
        
    def getCookies( self ):
        return self.cookies
    def getCookie( self, key, default=None ):
        try:
            return self.getCookies().get(key).value
        except AttributeError:
            return default

    def getHeader( self, key ):
        return self.headers.get( key )

    def normalizeQuery( self ):
        "forces the query to match the case rules specified by {html : normalize}"
        self.setQuery( normalize( self.getQuery() ) )

    def parseContent( self ):
        "parses POST request content"

        self.parseUriArgs( self.readContent() )

    def parseUriPath( self, uri ):
        "parses the path component of the request URI to determine query and action"
        steps = uri.split( "/" )
        
        if len( steps ) < 2:
            query, action = '', ''
        elif len( steps ) == 2:
            query, action = (steps[1] or ''), ''
        elif len( steps ) == 3:
            query, action = (steps[1] or ''), (steps[2] or '')
        else:
            raise InvalidRequestError( request = uri )
            
        if query is not None:
            query = uriunquote( query )
        
        self.query = query
        self.action = action

    def parseUriArgs( self, uriArgs ):
        "parses the argument component of the request URI to determine arguments" 
        for arg_pair in uriArgs.split('&'):
            key_and_value = arg_pair.split( "=", 1 )
            
            arg_key = key_and_value[0]
            
            if( len( key_and_value ) == 2 ):
                self.args[ arg_key ] = uriunquote( key_and_value[1] )
            else:
                self.args[ arg_key ] = ''
       
    def sendErrorResponse( self, error ):
        "Called when an error has occurred while processing the request"
        self.send_response( response.getCode() )
        self.send_header( "Content-type", "text/html" )
        
        self.sendData( self, error.asHtml() )

class ServerRequest( Request, BaseHTTPRequestHandler ):
    def __init__( self, *args, **kwargs ):
        Request.__init__( self )
        BaseHTTPRequestHandler.__init__( self, *args, **kwargs )
        
    def do_HEAD( self ):
        "responds to HEAD requests"

        try:
            self.parseUri( )
            self.parseCookies( )
            response = self.getWiki().respondTo( self )
        except CloudWiki, err:
            self.sendErrorResponse( err )
        else:
            self.sendResponseHeaders( response )
        
    def do_GET( self ):
        "responds to GET requests"

        try:
            self.parseUri( )
            self.parseCookies( )
            response = self.getWiki().respondTo( self )
        except CloudWiki, err:
            self.sendErrorResponse( err )
        else:
            self.sendResponseHeaders( response )
            self.sendResponseContent( response )
        
    def do_POST( self ):
        "responds to POST requests"

        try:
            self.parseUri( )
            self.parseContent( )
            self.parseCookies( )
        
            if not self.getAction():
                self.setAction( self.getArgs().get( 'action', '' ) )
        
            response = self.getWiki().respondTo( self )
        except CloudWiki, err:
            self.sendErrorResponse( response )
        else:
            self.sendResponseHeaders( response )
            self.sendResponseContent( response )

    def log_message( self, format, *args ):
        "overrides {BaseHTTPServer : BaseHTTPRequestHandler log_message} to use {Server logLine}"
        
        self.getServer().logLine(          
           "%s -- [%s] %s", 
            
            self.address_string(), 
            self.log_date_time_string(),
            format%args 
        )

    def getServer( self ):
        return self.server

    def getHeaders( self ):
        "returns a {rfc822 : Headers} instance"

        return self.headers

    def getUri( self ):
        "returns the request URI."
        return self.path

    def getWiki( self ):
        "returns the {wiki : Wiki} instance targeted by the request."
        #TODO: Implement in CgiRequest
        return self.getServer().getWiki()

    def getContentLength( self ):
        hv = self.getHeader( "Content-Length" )
        
        if hv == None: return 0
        
        try:
            hi = int( hv )
        except ValueError:
            return 0
        else:
            return hi
        
    def readContent( self ):
        content_left = self.getContentLength()
        content = StringIO()

        while content_left > 0:
            next = self.rfile.read( content_left )
            content_left -= len( next )
            content.write( next )

        return content.getvalue()

    def sendResponseHeaders( self, response ):
        "transmits the response code and headers"
        self.send_response( response.getCode() )

        for key, value in response.getHeaders():
            self.send_header( key, value )

        self.end_headers()

    def sendResponseContent( self, response ):
        "transmits the response content"
        self.sendData( response.getContent() )
                
    def sendLines( self, lines ):
        "transmits a list of lines"
        for line in lines:
            self.sendLine( line )
    
    def sendLine( self, line ):
        "transmits a single line, appending a \\n"
        self.sendData( line )
        self.sendData( "\n" )
    
    def sendData( self, data ):
        "transmits data to the http connection"
        self.wfile.write( data )

    def parseCookies( self ):
        "parses the cookies supplied by the request"
        for header in self.getHeaders().getallmatchingheaders( 'Cookie' ):
            self.cookies.load( header )

    def parseUri( self ):
        "parses the request URI into components understood by Cloud Wiki"
        path_and_args = self.getUri().split( "?", 1 )
                
        self.parseUriPath( path_and_args[ 0 ] )

        if len( path_and_args ) == 2 :
            self.parseUriArgs( path_and_args[1] )


class CgiRequest( Request ):
    __slots__ = ( 'stdin', 'stdout', 'environ', 'wiki' )
    
    def __init__( self, wiki, stdin = sys.stdin, stdout = sys.stdout, environ = os.environ ):
        Request.__init__( self )

        self.wiki = wiki
        self.stdin = stdin
        self.stdout = stdout
        self.environ = environ

    def getWiki( self ):
        return self.wiki
        
    def getContentLength( self ):
        ev = self.environ.get( "CONTENT_LENGTH" )
        if ev == None: return 0
        try:
            ei = int( ev )
        except ValueError, exc:
            return 0
        else:
            return ei
        
    def readContent( self ):
        "reads additional content from from stdin"
        content_left = self.getContentLength()
        content = StringIO()
        stdin = self.stdin
        
        while content_left > 0:
            next = stdin.read( content_left )
            content_left -= len( next )
            content.write( next )

        return content.getvalue()
        
    def sendResponseHeaders( self, response ):
        "transmits the response code and headers to stdout"
        self.sendData( "Status: " )
        self.sendLine( str( response.getCode() ) )

        for key, value in response.getHeaders():
            self.sendData( key )
            self.sendData( ": " )
            self.sendLine( str( value ) )

        self.sendLine( "" )
        
    def sendResponseContent( self, response ):
        "transmits the response content to stdout"
        self.sendData( response.getContent() )

    def sendLine( self, line ):
        self.sendData( line + "\n" )
        
    def sendData( self, data ):
        self.stdout.write( data )

    def runHeadRequest( self ):
        "responds to HEAD requests"

        try:
            self.parseUri( )
            self.parseCookies( )
        
            response = self.getWiki().respondTo( self )
        except CloudError, err:
            self.sendErrorResponse( err )
        else:
            self.sendResponseHeaders( response )
            
    def runGetRequest( self ):
        "responds to GET requests"

        try:
            self.parseUri( )
            self.parseCookies( )

            response = self.getWiki().respondTo( self )
        except CloudError, err:
            self.sendErrorResponse( err )
        else:
            self.sendResponseHeaders( response )
            self.sendResponseContent( response )
        
    def runPostRequest( self ):
        "responds to POST requests"
        try:
            self.parseUri( )
            self.parseContent( )
            self.parseCookies( )
            
            if not self.getAction():
                self.setAction( self.getArgs().get( 'action', '' ) )
            
            response = self.getWiki().respondTo( self )
        except CloudError, err:
            self.sendErrorResponse( err )
        else:
            self.sendResponseHeaders( response )
            self.sendResponseContent( response )

    def run( self ):
        mt = self.environ.get( "REQUEST_METHOD" )
        if mt == 'GET':
            self.runGetRequest()
        elif mt == 'POST':
            self.runPostRequest()
        elif mt == 'HEAD':
            self.runHeadRequest()

    def parseCookies( self ):
        "parses the cookies supplied by the request"
        
        self.cookies.load( self.environ.get( 'HTTP_COOKIE' ) or "" )

    def parseUri( self ):
        "parses the request URI into components understood by Cloud Wiki"
        path_info = self.environ.get( "PATH_INFO" ) or ""
        query_string = self.environ.get( "QUERY_STRING" ) or "" 
        
        self.parseUriPath( path_info )
        self.parseUriArgs( query_string )
        
class Response( object ):
    """
    Used to incrementally compose a response to a HTTP request by Cloud Wiki. This object is primarily
    used just for storage, and provides very little actual logic.
    
    ! Instance Properties:

    * code -- contains the http response code for this response; defaults to 200.
    * cookie -- contains a list of cookies to be sent to the client, with a fixed lifespan of 3600 seconds.
    * contentType -- contains a MIME type string. Defaults to "text/html"
    * content -- either a StringIO object for buffering content prior to transmission, or a string containing
    the entire content intended for transmission.  Defaults to a StringIO object.
    """
    
    __slots__ = ( "code", "cookies", "content", "contentType" )

    def __init__( self, code = 200):
        self.code = code
        self.cookies = []
        self.content = StringIO()
        self.contentType = "text/html" 

    def setCode( self, code ):
        self.code = code
    def getCode( self ):
        return self.code
                    
    def getContentType( self ):
        return self.contentType
    def setContentType( self, contentType ):
        self.contentType = contentType
        return self
        
    def getCookies( self ):
        for cookie in self.cookies:
            yield cookie
    def addCookie( self, key, value ):
        self.cookies.append( ( key, value ) )
        return self

    def getContent( self ):
        "returns the content of the response -- if the content attribute is a StringIO instance,"
        " this returns the value of that instance."
        
        if not isinstance( self.content, StringType ):
            self.content = self.content.getvalue()
        
        return self.content
    def setContent( self, content ):
        "wraps the supplied content in a StringIO instance and stores it in the content attribute."
        self.content = StringIO( content )
        return self
    def addContent( self, content ):
        "only usable when content is a StringIO object"
        self.content.write( content )
        return self

    def getHeaders( self ):
        "iterates over the response's headers"
        yield "Content-type", self.getContentType()
        yield "Response-length", len( self.getContent() )
        
        for key, value in self.getCookies():
            yield "Set-cookie", "%s=%s;Max-Age=3600" % ( key, value )

    def write( self, data ):
        "extends the content with the supplied data."
        if self.content is None: 
            self.setContent( data )
        else:
            self.content.write( data )
        return self
        
class Server( HTTPServer ):
    """
    A derivative of {BaseHTTPServer : HTTPServer} that uses {ServerRequest}s 
    to decode incoming requests, relays them to the correct fragment for 
    rendering a {Response}, and maintains information relevant to the wiki 
    for serving HTTP requests.
    
    ! Instance Properties:
    
    * wiki -- returns the {wiki : Wiki} instance associated with this server.
    * port -- returns the port number. Defaults to, in order, wiki.getHttpPort() or 8080.
    """

    __slots__ = (
        'port', 'wiki'
    )
    
    def __init__( self, wiki, port = None ):
        self.port = port or wiki.getHttpPort() or 8080
        self.wiki = wiki

        HTTPServer.__init__( self, ('', self.port), self.getHandlerClass() )

    def getWiki( self ):
        return self.wiki

    def getPort( self ):
        return self.port

    def logData( self, format, *args ):
        "a convenience function that instructs the wiki to log the supplied data"
        self.getWiki().logData( format, *args )
    def logLine( self, format, *args ):
        "a convenience function that instructs the wiki to log the supplied line"
        self.getWiki().logLine( format, *args )
        
    def getHandlerClass( self ):
        "returns the class that should be used for handling new requests"
        return ServerRequest
        
    def runForever( self ):
        "instructs the server to consume the current thread in serving requests, and will not return"
        self.serve_forever()
        
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.