| Index: third_party/twisted_8_1/twisted/web/server.py
|
| diff --git a/third_party/twisted_8_1/twisted/web/server.py b/third_party/twisted_8_1/twisted/web/server.py
|
| deleted file mode 100644
|
| index 9ee9579b707092122f9d82beeb69765a977ae140..0000000000000000000000000000000000000000
|
| --- a/third_party/twisted_8_1/twisted/web/server.py
|
| +++ /dev/null
|
| @@ -1,571 +0,0 @@
|
| -# -*- test-case-name: twisted.web.test.test_web -*-
|
| -
|
| -# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
|
| -# See LICENSE for details.
|
| -
|
| -
|
| -"""This is a web-server which integrates with the twisted.internet
|
| -infrastructure.
|
| -"""
|
| -
|
| -# System Imports
|
| -
|
| -import string
|
| -import types
|
| -import operator
|
| -import copy
|
| -import time
|
| -import os
|
| -from urllib import quote
|
| -try:
|
| - from twisted.protocols._c_urlarg import unquote
|
| -except ImportError:
|
| - from urllib import unquote
|
| -
|
| -#some useful constants
|
| -NOT_DONE_YET = 1
|
| -
|
| -# Twisted Imports
|
| -from twisted.spread import pb
|
| -from twisted.internet import defer, address, task
|
| -from twisted.web import http
|
| -from twisted.python import log, reflect, failure, components
|
| -from twisted import copyright
|
| -
|
| -# Sibling Imports
|
| -import error, resource
|
| -from twisted.web import util as webutil
|
| -
|
| -
|
| -# backwards compatability
|
| -date_time_string = http.datetimeToString
|
| -string_date_time = http.stringToDatetime
|
| -
|
| -# Support for other methods may be implemented on a per-resource basis.
|
| -supportedMethods = ('GET', 'HEAD', 'POST')
|
| -
|
| -
|
| -class UnsupportedMethod(Exception):
|
| - """Raised by a resource when faced with a strange request method.
|
| -
|
| - RFC 2616 (HTTP 1.1) gives us two choices when faced with this situtation:
|
| - If the type of request is known to us, but not allowed for the requested
|
| - resource, respond with NOT_ALLOWED. Otherwise, if the request is something
|
| - we don't know how to deal with in any case, respond with NOT_IMPLEMENTED.
|
| -
|
| - When this exception is raised by a Resource's render method, the server
|
| - will make the appropriate response.
|
| -
|
| - This exception's first argument MUST be a sequence of the methods the
|
| - resource *does* support.
|
| - """
|
| -
|
| - allowedMethods = ()
|
| -
|
| - def __init__(self, allowedMethods, *args):
|
| - Exception.__init__(self, allowedMethods, *args)
|
| - self.allowedMethods = allowedMethods
|
| -
|
| - if not operator.isSequenceType(allowedMethods):
|
| - why = "but my first argument is not a sequence."
|
| - s = ("First argument must be a sequence of"
|
| - " supported methods, %s" % (why,))
|
| - raise TypeError, s
|
| -
|
| -def _addressToTuple(addr):
|
| - if isinstance(addr, address.IPv4Address):
|
| - return ('INET', addr.host, addr.port)
|
| - elif isinstance(addr, address.UNIXAddress):
|
| - return ('UNIX', addr.name)
|
| - else:
|
| - return tuple(addr)
|
| -
|
| -class Request(pb.Copyable, http.Request, components.Componentized):
|
| -
|
| - site = None
|
| - appRootURL = None
|
| - __pychecker__ = 'unusednames=issuer'
|
| -
|
| - def __init__(self, *args, **kw):
|
| - http.Request.__init__(self, *args, **kw)
|
| - components.Componentized.__init__(self)
|
| - self.notifications = []
|
| -
|
| - def getStateToCopyFor(self, issuer):
|
| - x = self.__dict__.copy()
|
| - del x['transport']
|
| - # XXX refactor this attribute out; it's from protocol
|
| - # del x['server']
|
| - del x['channel']
|
| - del x['content']
|
| - del x['site']
|
| - self.content.seek(0, 0)
|
| - x['content_data'] = self.content.read()
|
| - x['remote'] = pb.ViewPoint(issuer, self)
|
| -
|
| - # Address objects aren't jellyable
|
| - x['host'] = _addressToTuple(x['host'])
|
| - x['client'] = _addressToTuple(x['client'])
|
| -
|
| - return x
|
| -
|
| - # HTML generation helpers
|
| -
|
| - def sibLink(self, name):
|
| - "Return the text that links to a sibling of the requested resource."
|
| - if self.postpath:
|
| - return (len(self.postpath)*"../") + name
|
| - else:
|
| - return name
|
| -
|
| - def childLink(self, name):
|
| - "Return the text that links to a child of the requested resource."
|
| - lpp = len(self.postpath)
|
| - if lpp > 1:
|
| - return ((lpp-1)*"../") + name
|
| - elif lpp == 1:
|
| - return name
|
| - else: # lpp == 0
|
| - if len(self.prepath) and self.prepath[-1]:
|
| - return self.prepath[-1] + '/' + name
|
| - else:
|
| - return name
|
| -
|
| - def process(self):
|
| - "Process a request."
|
| -
|
| - # get site from channel
|
| - self.site = self.channel.site
|
| -
|
| - # set various default headers
|
| - self.setHeader('server', version)
|
| - self.setHeader('date', http.datetimeToString())
|
| - self.setHeader('content-type', "text/html")
|
| -
|
| - # Resource Identification
|
| - self.prepath = []
|
| - self.postpath = map(unquote, string.split(self.path[1:], '/'))
|
| - try:
|
| - resrc = self.site.getResourceFor(self)
|
| - self.render(resrc)
|
| - except:
|
| - self.processingFailed(failure.Failure())
|
| -
|
| -
|
| - def render(self, resrc):
|
| - try:
|
| - body = resrc.render(self)
|
| - except UnsupportedMethod, e:
|
| - allowedMethods = e.allowedMethods
|
| - if (self.method == "HEAD") and ("GET" in allowedMethods):
|
| - # We must support HEAD (RFC 2616, 5.1.1). If the
|
| - # resource doesn't, fake it by giving the resource
|
| - # a 'GET' request and then return only the headers,
|
| - # not the body.
|
| - log.msg("Using GET to fake a HEAD request for %s" %
|
| - (resrc,))
|
| - self.method = "GET"
|
| - body = resrc.render(self)
|
| -
|
| - if body is NOT_DONE_YET:
|
| - log.msg("Tried to fake a HEAD request for %s, but "
|
| - "it got away from me." % resrc)
|
| - # Oh well, I guess we won't include the content length.
|
| - else:
|
| - self.setHeader('content-length', str(len(body)))
|
| -
|
| - self.write('')
|
| - self.finish()
|
| - return
|
| -
|
| - if self.method in (supportedMethods):
|
| - # We MUST include an Allow header
|
| - # (RFC 2616, 10.4.6 and 14.7)
|
| - self.setHeader('Allow', allowedMethods)
|
| - s = ('''Your browser approached me (at %(URI)s) with'''
|
| - ''' the method "%(method)s". I only allow'''
|
| - ''' the method%(plural)s %(allowed)s here.''' % {
|
| - 'URI': self.uri,
|
| - 'method': self.method,
|
| - 'plural': ((len(allowedMethods) > 1) and 's') or '',
|
| - 'allowed': string.join(allowedMethods, ', ')
|
| - })
|
| - epage = error.ErrorPage(http.NOT_ALLOWED,
|
| - "Method Not Allowed", s)
|
| - body = epage.render(self)
|
| - else:
|
| - epage = error.ErrorPage(http.NOT_IMPLEMENTED, "Huh?",
|
| - """I don't know how to treat a"""
|
| - """ %s request."""
|
| - % (self.method))
|
| - body = epage.render(self)
|
| - # end except UnsupportedMethod
|
| -
|
| - if body == NOT_DONE_YET:
|
| - return
|
| - if type(body) is not types.StringType:
|
| - body = error.ErrorPage(http.INTERNAL_SERVER_ERROR,
|
| - "Request did not return a string",
|
| - "Request: "+html.PRE(reflect.safe_repr(self))+"<br />"+
|
| - "Resource: "+html.PRE(reflect.safe_repr(resrc))+"<br />"+
|
| - "Value: "+html.PRE(reflect.safe_repr(body))).render(self)
|
| -
|
| - if self.method == "HEAD":
|
| - if len(body) > 0:
|
| - # This is a Bad Thing (RFC 2616, 9.4)
|
| - log.msg("Warning: HEAD request %s for resource %s is"
|
| - " returning a message body."
|
| - " I think I'll eat it."
|
| - % (self, resrc))
|
| - self.setHeader('content-length', str(len(body)))
|
| - self.write('')
|
| - else:
|
| - self.setHeader('content-length', str(len(body)))
|
| - self.write(body)
|
| - self.finish()
|
| -
|
| - def processingFailed(self, reason):
|
| - log.err(reason)
|
| - if self.site.displayTracebacks:
|
| - body = ("<html><head><title>web.Server Traceback (most recent call last)</title></head>"
|
| - "<body><b>web.Server Traceback (most recent call last):</b>\n\n"
|
| - "%s\n\n</body></html>\n"
|
| - % webutil.formatFailure(reason))
|
| - else:
|
| - body = ("<html><head><title>Processing Failed</title></head><body>"
|
| - "<b>Processing Failed</b></body></html>")
|
| -
|
| - self.setResponseCode(http.INTERNAL_SERVER_ERROR)
|
| - self.setHeader('content-type',"text/html")
|
| - self.setHeader('content-length', str(len(body)))
|
| - self.write(body)
|
| - self.finish()
|
| - return reason
|
| -
|
| - def notifyFinish(self):
|
| - """Notify when finishing the request
|
| -
|
| - @return: A deferred. The deferred will be triggered when the
|
| - request is finished -- with a C{None} value if the request
|
| - finishes successfully or with an error if the request is stopped
|
| - by the client.
|
| - """
|
| - self.notifications.append(defer.Deferred())
|
| - return self.notifications[-1]
|
| -
|
| - def connectionLost(self, reason):
|
| - for d in self.notifications:
|
| - d.errback(reason)
|
| - self.notifications = []
|
| -
|
| - def finish(self):
|
| - http.Request.finish(self)
|
| - for d in self.notifications:
|
| - d.callback(None)
|
| - self.notifications = []
|
| -
|
| - def view_write(self, issuer, data):
|
| - """Remote version of write; same interface.
|
| - """
|
| - self.write(data)
|
| -
|
| - def view_finish(self, issuer):
|
| - """Remote version of finish; same interface.
|
| - """
|
| - self.finish()
|
| -
|
| - def view_addCookie(self, issuer, k, v, **kwargs):
|
| - """Remote version of addCookie; same interface.
|
| - """
|
| - self.addCookie(k, v, **kwargs)
|
| -
|
| - def view_setHeader(self, issuer, k, v):
|
| - """Remote version of setHeader; same interface.
|
| - """
|
| - self.setHeader(k, v)
|
| -
|
| - def view_setLastModified(self, issuer, when):
|
| - """Remote version of setLastModified; same interface.
|
| - """
|
| - self.setLastModified(when)
|
| -
|
| - def view_setETag(self, issuer, tag):
|
| - """Remote version of setETag; same interface.
|
| - """
|
| - self.setETag(tag)
|
| -
|
| - def view_setResponseCode(self, issuer, code):
|
| - """Remote version of setResponseCode; same interface.
|
| - """
|
| - self.setResponseCode(code)
|
| -
|
| - def view_registerProducer(self, issuer, producer, streaming):
|
| - """Remote version of registerProducer; same interface.
|
| - (requires a remote producer.)
|
| - """
|
| - self.registerProducer(_RemoteProducerWrapper(producer), streaming)
|
| -
|
| - def view_unregisterProducer(self, issuer):
|
| - self.unregisterProducer()
|
| -
|
| - ### these calls remain local
|
| -
|
| - session = None
|
| -
|
| - def getSession(self, sessionInterface = None):
|
| - # Session management
|
| - if not self.session:
|
| - cookiename = string.join(['TWISTED_SESSION'] + self.sitepath, "_")
|
| - sessionCookie = self.getCookie(cookiename)
|
| - if sessionCookie:
|
| - try:
|
| - self.session = self.site.getSession(sessionCookie)
|
| - except KeyError:
|
| - pass
|
| - # if it still hasn't been set, fix it up.
|
| - if not self.session:
|
| - self.session = self.site.makeSession()
|
| - self.addCookie(cookiename, self.session.uid, path='/')
|
| - self.session.touch()
|
| - if sessionInterface:
|
| - return self.session.getComponent(sessionInterface)
|
| - return self.session
|
| -
|
| - def _prePathURL(self, prepath):
|
| - port = self.getHost().port
|
| - if self.isSecure():
|
| - default = 443
|
| - else:
|
| - default = 80
|
| - if port == default:
|
| - hostport = ''
|
| - else:
|
| - hostport = ':%d' % port
|
| - return 'http%s://%s%s/%s' % (
|
| - self.isSecure() and 's' or '',
|
| - self.getRequestHostname(),
|
| - hostport,
|
| - '/'.join([quote(segment, safe='') for segment in prepath]))
|
| -
|
| - def prePathURL(self):
|
| - return self._prePathURL(self.prepath)
|
| -
|
| - def URLPath(self):
|
| - from twisted.python import urlpath
|
| - return urlpath.URLPath.fromRequest(self)
|
| -
|
| - def rememberRootURL(self):
|
| - """
|
| - Remember the currently-processed part of the URL for later
|
| - recalling.
|
| - """
|
| - url = self._prePathURL(self.prepath[:-1])
|
| - self.appRootURL = url
|
| -
|
| - def getRootURL(self):
|
| - """
|
| - Get a previously-remembered URL.
|
| - """
|
| - return self.appRootURL
|
| -
|
| -
|
| -class _RemoteProducerWrapper:
|
| - def __init__(self, remote):
|
| - self.resumeProducing = remote.remoteMethod("resumeProducing")
|
| - self.pauseProducing = remote.remoteMethod("pauseProducing")
|
| - self.stopProducing = remote.remoteMethod("stopProducing")
|
| -
|
| -
|
| -class Session(components.Componentized):
|
| - """
|
| - A user's session with a system.
|
| -
|
| - This utility class contains no functionality, but is used to
|
| - represent a session.
|
| -
|
| - @ivar sessionTimeout: timeout of a session, in seconds.
|
| - @ivar loopFactory: factory for creating L{task.LoopingCall}. Mainly for
|
| - testing.
|
| - """
|
| - sessionTimeout = 900
|
| - loopFactory = task.LoopingCall
|
| -
|
| - def __init__(self, site, uid):
|
| - """
|
| - Initialize a session with a unique ID for that session.
|
| - """
|
| - components.Componentized.__init__(self)
|
| - self.site = site
|
| - self.uid = uid
|
| - self.expireCallbacks = []
|
| - self.checkExpiredLoop = None
|
| - self.touch()
|
| - self.sessionNamespaces = {}
|
| -
|
| -
|
| - def startCheckingExpiration(self, lifetime):
|
| - """
|
| - Start expiration tracking.
|
| -
|
| - @type lifetime: C{int} or C{float}
|
| - @param lifetime: The number of seconds this session is allowed to be
|
| - idle before it expires.
|
| -
|
| - @return: C{None}
|
| - """
|
| - self.checkExpiredLoop = self.loopFactory(self.checkExpired)
|
| - self.checkExpiredLoop.start(lifetime, now=False)
|
| -
|
| -
|
| - def notifyOnExpire(self, callback):
|
| - """
|
| - Call this callback when the session expires or logs out.
|
| - """
|
| - self.expireCallbacks.append(callback)
|
| -
|
| -
|
| - def expire(self):
|
| - """
|
| - Expire/logout of the session.
|
| - """
|
| - del self.site.sessions[self.uid]
|
| - for c in self.expireCallbacks:
|
| - c()
|
| - self.expireCallbacks = []
|
| - if self.checkExpiredLoop is not None:
|
| - self.checkExpiredLoop.stop()
|
| - # Break reference cycle.
|
| - self.checkExpiredLoop = None
|
| -
|
| -
|
| - def _getTime(self):
|
| - """
|
| - Return current time used for session validity.
|
| - """
|
| - return time.time()
|
| -
|
| -
|
| - def touch(self):
|
| - """
|
| - Notify session modification.
|
| - """
|
| - self.lastModified = self._getTime()
|
| -
|
| -
|
| - def checkExpired(self):
|
| - """
|
| - Is it time for me to expire?
|
| -
|
| - If I haven't been touched in fifteen minutes, I will call my
|
| - expire method.
|
| - """
|
| - # If I haven't been touched in 15 minutes:
|
| - if self._getTime() - self.lastModified > self.sessionTimeout:
|
| - if self.uid in self.site.sessions:
|
| - self.expire()
|
| -
|
| -
|
| -version = "TwistedWeb/%s" % copyright.version
|
| -
|
| -
|
| -class Site(http.HTTPFactory):
|
| - """
|
| - A web site: manage log, sessions, and resources.
|
| -
|
| - @ivar counter: increment value used for generating unique sessions ID.
|
| - @ivar requestFactory: factory creating requests objects. Default to
|
| - L{Request}.
|
| - @ivar displayTracebacks: if set, Twisted internal errors are displayed on
|
| - rendered pages. Default to C{True}.
|
| - @ivar sessionFactory: factory for sessions objects. Default to L{Session}.
|
| - @ivar sessionCheckTime: interval between each check of session expiration.
|
| - """
|
| - counter = 0
|
| - requestFactory = Request
|
| - displayTracebacks = True
|
| - sessionFactory = Session
|
| - sessionCheckTime = 1800
|
| -
|
| - def __init__(self, resource, logPath=None, timeout=60*60*12):
|
| - """
|
| - Initialize.
|
| - """
|
| - http.HTTPFactory.__init__(self, logPath=logPath, timeout=timeout)
|
| - self.sessions = {}
|
| - self.resource = resource
|
| -
|
| - def _openLogFile(self, path):
|
| - from twisted.python import logfile
|
| - return logfile.LogFile(os.path.basename(path), os.path.dirname(path))
|
| -
|
| - def __getstate__(self):
|
| - d = self.__dict__.copy()
|
| - d['sessions'] = {}
|
| - return d
|
| -
|
| - def _mkuid(self):
|
| - """
|
| - (internal) Generate an opaque, unique ID for a user's session.
|
| - """
|
| - import md5, random
|
| - self.counter = self.counter + 1
|
| - return md5.new("%s_%s" % (str(random.random()) , str(self.counter))).hexdigest()
|
| -
|
| - def makeSession(self):
|
| - """
|
| - Generate a new Session instance, and store it for future reference.
|
| - """
|
| - uid = self._mkuid()
|
| - session = self.sessions[uid] = self.sessionFactory(self, uid)
|
| - session.startCheckingExpiration(self.sessionCheckTime)
|
| - return session
|
| -
|
| - def getSession(self, uid):
|
| - """
|
| - Get a previously generated session, by its unique ID.
|
| - This raises a KeyError if the session is not found.
|
| - """
|
| - return self.sessions[uid]
|
| -
|
| - def buildProtocol(self, addr):
|
| - """
|
| - Generate a channel attached to this site.
|
| - """
|
| - channel = http.HTTPFactory.buildProtocol(self, addr)
|
| - channel.requestFactory = self.requestFactory
|
| - channel.site = self
|
| - return channel
|
| -
|
| - isLeaf = 0
|
| -
|
| - def render(self, request):
|
| - """
|
| - Redirect because a Site is always a directory.
|
| - """
|
| - request.redirect(request.prePathURL() + '/')
|
| - request.finish()
|
| -
|
| - def getChildWithDefault(self, pathEl, request):
|
| - """
|
| - Emulate a resource's getChild method.
|
| - """
|
| - request.site = self
|
| - return self.resource.getChildWithDefault(pathEl, request)
|
| -
|
| - def getResourceFor(self, request):
|
| - """
|
| - Get a resource for a request.
|
| -
|
| - This iterates through the resource heirarchy, calling
|
| - getChildWithDefault on each resource it finds for a path element,
|
| - stopping when it hits an element where isLeaf is true.
|
| - """
|
| - request.site = self
|
| - # Sitepath is used to determine cookie names between distributed
|
| - # servers and disconnected sites.
|
| - request.sitepath = copy.copy(request.prepath)
|
| - return resource.getChildForRequest(self.resource, request)
|
| -
|
| -
|
| -import html
|
| -
|
|
|