summaryrefslogtreecommitdiffstats
path: root/python/tests/http.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/tests/http.py')
-rw-r--r--python/tests/http.py935
1 files changed, 0 insertions, 935 deletions
diff --git a/python/tests/http.py b/python/tests/http.py
deleted file mode 100644
index 27e8a99a..00000000
--- a/python/tests/http.py
+++ /dev/null
@@ -1,935 +0,0 @@
-# -*- coding: UTF-8 -*-
-
-
-# HTTP Client and Server Enhanced Classes
-# By: Frederic Peters <fpeters@entrouvert.com>
-# Emmanuel Raviart <eraviart@entrouvert.com>
-#
-# Copyright (C) 2004 Entr'ouvert
-# http://www.entrouvert.org
-#
-# 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.
-
-
-"""HTTP client and server enhanced classes
-
-Features:
-- HTTPS using OpenSSL;
-- web sessions (with or without cookie);
-- user authentication (support of basic HTTP-authentication, X.509v3 certificate authentication,
- HTML based authentication, etc).
-"""
-
-
-import base64
-import BaseHTTPServer
-import Cookie
-import cStringIO
-import gzip
-import httplib
-import os
-import socket
-import SocketServer
-import sys
-import time
-
-try:
- from OpenSSL import SSL
-except ImportError:
- SSL = None
-
-import abstractweb
-import submissions
-
-
-try:
- logger
-except NameError:
- logger = None
-if logger is None:
- import logging as logger
-
-
-class BaseHTTPSRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
- def setup(self):
- """
- We need to use socket._fileobject Because SSL.Connection
- doesn't have a 'dup'. Not exactly sure WHY this is, but
- this is backed up by comments in socket.py and SSL/connection.c
- """
-
- self.connection = self.request # for doPOST
- self.rfile = socket._fileobject(self.request, 'rb', self.rbufsize)
- self.wfile = socket._fileobject(self.request, 'wb', self.wbufsize)
-
-
-class BaseHTTPSServer(SocketServer.TCPServer):
- def __init__(self, server_address, RequestHandlerClass, privateKeyFilePath,
- certificateFilePath, peerCaCertificateFile = None,
- certificateChainFilePath = None, verifyClient = None):
- SocketServer.BaseServer.__init__(self, server_address, RequestHandlerClass)
- self.verifyClient = verifyClient
-
- ctx = SSL.Context(SSL.SSLv23_METHOD)
- nVerify = SSL.VERIFY_NONE
- ctx.set_options(SSL.OP_NO_SSLv2)
-
- ctx.use_privatekey_file(privateKeyFilePath)
- ctx.use_certificate_file(certificateFilePath)
- if peerCaCertificateFile:
- ctx.load_verify_locations(peerCaCertificateFile)
- if certificateChainFilePath:
- ctx.use_certificate_chain_file(certificateChainFilePath)
- if verifyClient == 'require':
- nVerify |= SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT
- elif verifyClient in ('optional', 'optional_on_ca'):
- nVerify |= SSL.VERIFY_PEER
- ctx.set_verify(nVerify, self.verifyCallback)
-
- self.socket = SSL.Connection(ctx, socket.socket(self.address_family, self.socket_type))
- self.server_bind()
- self.server_activate()
-
- def verifyCallback(self, connection, x509Object, errorNumber, errorDepth, returnCode):
- logger.info('http.HttpsConnection(%s, %s, %s, %s, %s, %s)' % (
- self, connection, x509Object, errorNumber, errorDepth, returnCode))
- return returnCode
-
- #~ def server_bind(self):
- #~ """Override server_bind to store the server name."""
-
- #~ SocketServer.TCPServer.server_bind(self)
- #~ host, port = self.socket.getsockname()[:2]
- #~ self.server_name = socket.getfqdn(host)
- #~ self.server_port = port
-
-
-class HttpRequest(abstractweb.HttpRequestMixin, object):
- handler = None
- submission = None
-
- def __init__(self, handler):
- self.handler = handler
- self.submission = submissions.readSubmission(self.handler)
-
- def getBody(self):
- return self.submission.readFile()
-
- def getHeaders(self):
- return self.handler.headers
-
- def getMethod(self):
- return self.handler.command
-
- def getPath(self):
- return self.pathAndQuery.split('?', 1)[0]
-
- def getPathAndQuery(self):
- return self.handler.path
-
- def getQuery(self):
- splitedPathAndQuery = self.pathAndQuery.split('?', 1)
- if len(splitedPathAndQuery) > 1:
- return splitedPathAndQuery[1]
- else:
- return ''
-
- def getScheme(self):
- return self.handler.scheme
-
- def getUrl(self):
- return "%s://%s%s" % (self.scheme, self.headers.get('Host'), self.pathAndQuery)
-
- body = property(getBody)
- headers = property(getHeaders)
- method = property(getMethod)
- path = property(getPath)
- pathAndQuery = property(getPathAndQuery)
- query = property(getQuery)
- scheme = property(getScheme)
- url = property(getUrl)
-
-
-class HttpResponse(abstractweb.HttpResponseMixin, object):
- def send(self, httpRequestHandler):
- statusCode = self.statusCode
- if statusCode == 401:
- return self.send401(httpRequestHandler)
- elif statusCode == 404:
- return self.send404(httpRequestHandler)
- assert statusCode in (200, 207)
- if self.headers is None:
- headers = {}
- else:
- headers = self.headers.copy()
- if time.time() > httpRequestHandler.socketCreationTime + 300:
- headers['Connection'] = 'close'
- elif not httpRequestHandler.close_connection:
- headers['Connection'] = 'Keep-Alive'
- # TODO: Could also output Content-MD5.
- lastModified = headers.get('Last-Modified')
- ifModifiedSince = httpRequestHandler.httpRequest.headers.get('If-Modified-Since')
- if lastModified and ifModifiedSince:
- # We don't want to use bandwith if the file was not modified.
- try:
- lastModifiedTime = time.strptime(lastModified[:25], '%a, %d %b %Y %H:%M:%S')
- ifModifiedSinceTime = time.strptime(ifModifiedSince[:25], '%a, %d %b %Y %H:%M:%S')
- if lastModifiedTime[:8] <= ifModifiedSinceTime[:8]:
- httpRequestHandler.send_response(304, 'Not Modified.')
- for key in ('Connection', 'Content-Location'):
- if key in headers:
- httpRequestHandler.send_header(key, headers[key])
- httpRequestHandler.setCookie()
- httpRequestHandler.end_headers()
- return
- except (ValueError, KeyError):
- pass
- data = self.body
- if data is None:
- data = ''
- if isinstance(data, basestring):
- dataFile = None
- dataSize = len(data)
- else:
- dataFile = data
- data = ''
- if hasattr(dataFile, 'fileno'):
- dataSize = os.fstat(dataFile.fileno())[6]
- else:
- # For StringIO and cStringIO classes.
- dataSize = len(dataFile.getvalue())
- if dataFile is not None:
- assert not data
- data = dataFile.read(1048576) # Read first MB chunk
- contentType = headers.get('Content-Type')
- if contentType and contentType.split(';')[0] == 'text/html' and data.startswith('<?xml'):
- # Internet Explorer 6 renders the page differently when they start with <?xml...>, so
- # skip it.
- i = data.find('\n')
- if i > 0:
- data = data[i + 1:]
- else:
- i = data.find('>')
- if i > 0:
- data = data[i + 1:]
- dataSize -= i + 1
- # Compress data if possible and if data is not too big.
- acceptEncoding = httpRequestHandler.httpRequest.headers.get('Accept-Encoding', '')
- if 0 < dataSize < 1048576 and 'gzip' in acceptEncoding \
- and 'gzip;q=0' not in acceptEncoding:
- # Since dataSize < 1 MB, the data is fully contained in string.
- zbuf = cStringIO.StringIO()
- zfile = gzip.GzipFile(mode = 'wb', fileobj = zbuf)
- zfile.write(data)
- zfile.close()
- data = zbuf.getvalue()
- dataSize = len(data)
- headers['Content-Encoding'] = 'gzip'
- headers['Content-Length'] = '%d' % dataSize
- statusMessages = {
- 200: 'OK',
- 207: 'Multi-Status',
- }
- assert statusCode in statusMessages, 'Unknown status code %d.' % statusCode
- if httpRequestHandler.httpAuthenticationLogoutTrick and statusCode == 200:
- statusCode = 401
- statusMessage = 'Access Unauthorized'
- headers['WWW-Authenticate'] = 'Basic realm="%s"' % httpRequestHandler.realm
- else:
- statusMessage = statusMessages[statusCode]
- httpRequestHandler.send_response(statusCode, statusMessage)
- for key, value in headers.items():
- httpRequestHandler.send_header(key, value)
- httpRequestHandler.setCookie()
- httpRequestHandler.end_headers()
- if httpRequestHandler.httpRequest.method != 'HEAD' and dataSize > 0:
- outputFile = httpRequestHandler.wfile
- if data:
- outputFile.write(data)
- if dataFile is not None:
- while True:
- chunk = dataFile.read(1048576) # 1 MB chunk
- if not chunk:
- break
- outputFile.write(chunk)
-
- def send401(self, httpRequestHandler):
- logger.info(self.statusMessage)
- data = '<html><body>%s</body></html>' % self.statusMessage
- headers = {}
- if httpRequestHandler.useHttpAuthentication == 'not this time':
- del httpRequestHandler.useHttpAuthentication
- if httpRequestHandler.useHttpAuthentication != True:
- httpRequestHandler.useHttpAuthentication = True
- elif httpRequestHandler.useHttpAuthentication:
- headers["WWW-Authenticate"] = 'Basic realm="%s"' % httpRequestHandler.realm
- return httpRequestHandler.send_error(
- self.statusCode, self.statusMessage, data, headers, setCookie = True)
-
- def send404(self, httpRequestHandler):
- logger.info(self.statusMessage)
- data = '<html><body>%s</body></html>' % self.statusMessage
- return httpRequestHandler.send_error(
- self.statusCode, self.statusMessage, data, setCookie = True)
-
-
-class HttpRequestHandlerMixin(abstractweb.HttpRequestHandlerMixin):
- canUseCookie = False
- cookie = None
- httpAuthenticationLogoutTrick = False
- HttpResponse = HttpResponse # Class
- socketCreationTime = None
- protocol_version = 'HTTP/1.1'
- realm = 'HttpRequestHandlerMixin Web Site'
- server_version = 'HttpRequestHandlerMixin/1.0'
- site = None # Class variable
- testCookieSupport = False
- useHttpAuthentication = True
-
- def createSession(self):
- session = abstractweb.HttpRequestHandlerMixin.createSession(self)
- if self.canUseCookie:
- self.testCookieSupport = True
- return session
-
- def handle(self):
- """Handle multiple requests if necessary."""
- self.socketCreationTime = time.time()
- try:
- try:
- self.close_connection = True
- self.handle_one_request()
- while not self.close_connection:
- self.handle_one_request()
- except socket.timeout:
- pass
- except KeyboardInterrupt:
- raise
- except SSL.ZeroReturnError:
- pass
- except SSL.Error, exception:
- logger.debug('SSL error in handle. Error = %s, %s' % (exception, exception[0]))
- raise # FIXME
- if exception[0] == ('PEM routines', 'PEM_read_bio', 'no start line'):
- pass
- else:
- self.outputUnknownException()
- except:
- self.outputUnknownException()
- finally:
- del self.socketCreationTime
-
- def handle_one_request(self):
- """Handle a single HTTP request."""
- self.raw_requestline = self.rfile.readline()
- if not self.raw_requestline:
- self.close_connection = True
- return
- if not self.parse_request(): # An error code has been sent, just exit
- return
- logger.info(self.raw_requestline.strip())
- logger.debug(str(self.headers))
-
- # The server isn't forked nor threaded, so we don't want to keep connections open, to avoid
- # dead-locks which occur for example when the connection with the navigator to the identity
- # provider is kept open, while a service provider sends a SOAP request to the identity
- # provider.
- # Remove this line for forked or threaded servers.
- self.close_connection = True
-
- self.httpRequest = HttpRequest(self)
-
- # Retrieve the session and user, if possible.
-
- session = None
- sessionToken = None
- user = None
-
- # Handle X.509 certificate authentication.
- if hasattr(self.connection, 'get_peer_certificate'):
- clientCertificate = self.connection.get_peer_certificate()
- if clientCertificate:
- user = self.site.authenticateX509User(clientCertificate)
- if user is None:
- logger.info('Unknown certificate (serial number = %s)'
- % clientCertificate.get_serial_number())
- else:
- sessionToken = user.sessionToken
- if sessionToken is not None:
- session = self.site.sessions.get(sessionToken)
- if session is None:
- sessionToken = None
- del user.sessionToken
- else:
- # For security reasons, we want to minimize the publication of
- # session token (it is better not to store it in a cookie or in
- # URLs). The client need to send the certificate each time, for the
- # session to continue.
- if session.publishToken:
- del session.publishToken
-
- # Handle HTTP authentication.
- authorization = self.httpRequest.headers.get('authorization')
- if self.httpRequest.hasQueryField('login') and not authorization \
- and self.useHttpAuthentication:
- # Ask for HTTP authentication.
- return self.outputErrorUnauthorized(self.httpRequest.path)
- if self.httpRequest.hasQueryField('logout') and authorization:
- # Since HTTP authentication provides no way to logout, we send a status
- # Unauthorized to force the user to press the cancel button. But instead of
- # sending an error page immediately, we send the real page, so the user will see
- # the page instead of an error message.
- authorization = None
- self.httpAuthenticationLogoutTrick = True
- if authorization:
- try:
- authenticationScheme, credentials = authorization.split(None, 1)
- except ValueError:
- return self.outputErrorUnauthorized(self.httpRequest.path)
- authenticationScheme = authenticationScheme.lower()
- if authenticationScheme == 'basic':
- loginAndPassword = base64.decodestring(credentials)
- try:
- login, password = loginAndPassword.split(':', 1)
- except:
- login = loginAndPassword
- password = ''
- logger.debug('Basic authentication: login = "%s" / password = "%s"' % (
- login, password))
- if password:
- user = self.site.authenticateLoginPasswordUser(login, password)
- if user is None:
- logger.info('Unknown user (login = "%s" / password = "%s")' % (
- login, password))
- return self.outputErrorUnauthorized(self.httpRequest.path)
- else:
- sessionToken = user.sessionToken
- if sessionToken is not None:
- session = self.site.sessions.get(sessionToken)
- if session is None:
- sessionToken = None
- del user.sessionToken
- else:
- # For security reasons, we want to minimize the publication of
- # session token (it is better not to store it in a cookie or in
- # URLs). The client need to send the certificate each time, for the
- # session to continue.
- if session.publishToken:
- del session.publishToken
- elif login:
- # No password was given. Assume login contains a session token.
- # TODO: sanity chek on login
- sessionToken = login
- session = self.site.sessions.get(sessionToken)
- if session is not None and session.userId is not None:
- user = self.site.users.get(session.userId)
- if user is not None and user.sessionToken != session.token:
- # Sanity check.
- user.sessionToken = session.token
- else:
- logger.info('Unknown authentication scheme = %s' % authenticationScheme)
- return self.outputErrorUnauthorized(self.httpRequest.path)
-
- # Handle use of cookies, session and user.
- cookie = None
- cookieContent = {}
- if self.httpRequest.headers.has_key('Cookie'):
- logger.debug('Cookie received:')
- cookie = Cookie.SimpleCookie(
- self.httpRequest.headers['Cookie'])
- for k, v in cookie.items():
- cookieContent[k] = v.value
- logger.debug(' %s = %s' % (k, cookieContent[k]))
- self.cookie = cookie
-
- sessionToken = None
- sessionTokenInCookie = False
- if self.httpRequest.hasQueryField('sessionToken'):
- sessionToken = self.httpRequest.getQueryField('sessionToken')
- if not sessionToken:
- sessionToken = None
- if session is not None and sessionToken != session.token:
- sessionToken = None
- if cookieContent.has_key('sessionToken'):
- cookieSessionToken = cookieContent['sessionToken']
- if cookieSessionToken:
- if session is None or cookieSessionToken == session.token:
- if sessionToken is None:
- sessionToken = cookieSessionToken
- if cookieSessionToken == sessionToken:
- sessionTokenInCookie = True
- canUseCookie = True
- if session is None and sessionToken is not None:
- session = self.site.sessions.get(sessionToken)
- if session is None:
- sessionToken = None
- sessionTokenInCookie = False
- else:
- if user is None:
- if session.userId is not None:
- user = self.site.users.get(session.userId)
- if user is not None and user.sessionToken != sessionToken:
- # Sanity check.
- user.sessionToken = sessionToken
- else:
- # The user has been authenticated (using HTTP or X.509 authentication), but the
- # associated session didn't exist (or was too old, or...). So, update
- # its sessionToken.
- user.sessionToken = sessionToken
- # For security reasons, we want to minimize the publication of session
- # token (it is better not to store it in a cookie or in URLs).
- if session.publishToken:
- del session.publishToken
- if session is None and user is not None:
- # The user has been authenticated (using HTTP or X.509 authentication), but the session
- # doesn't exist yet (or was too old, or...). Create a new session.
- session = self.createSession()
- # For security reasons, we want to minimize the publication of session
- # token (it is better not to store it in a cookie or in URLs).
- # session.publishToken = False # False is the default value.
- session.userId = user.uniqueId
- user.sessionToken = session.token
- else:
- self.session = session
- if session is not None:
- if not sessionTokenInCookie:
- # The sessionToken is valid but is not stored in the cookie. So, don't try to
- # use cookie.
- canUseCookie = False
- logger.debug('Session: %s' % session.simpleLabel)
- self.canUseCookie = canUseCookie
- self.user = user
- if user is not None:
- logger.debug('User: %s' % user.simpleLabel)
-
- # Now, the HTTP request handler has done everything it could done. Transfer the processing
- # to the site.
-
- try:
- self.site.handleHttpRequestHandler(self)
- except IOError:
- logger.exception('An exception occured:')
- path = self.path.split('?')[0]
- return self.outputErrorNotFound(path)
-
- def log_message(self, format, *arguments):
- """Override BaseHTTPServer.HttpRequestHandler method to use logger.
-
- Do not use. Use logger instead.
- """
-
- logger.info('%s - - [%s] %s' % (
- self.address_string(), self.log_date_time_string(), format % arguments))
-
-## def outputAlert(self, data, title = None, url = None):
-## import html
-## if title is None:
-## title = N_('Alert')
-## # FIXME: Handle XSLT template.
-## if url:
-## buttonsBar = html.div(class_ = 'buttons-bar')
-## actionButtonsBar = html.span(class_ = 'action-buttons-bar')
-## buttonsBar.append(actionButtonsBar)
-## actionButtonsBar.append(html.a(_('OK'), class_ = 'button', href = url))
-## else:
-## buttonsBar = None
-## layout = html.html(
-## html.head(html.title(_(title))),
-## html.body(
-## html.p(_(data), class_ = 'alert'),
-## buttonsBar,
-## ),
-## )
-## self.outputData(layout.serialize(), contentLocation = None, mimeType = 'text/html')
-
-## def outputData(self, data, contentLocation = None, headers = None, mimeType = None,
-## modificationTime = None, successCode = 200):
-## # Session and user must be saved before responding. Otherwise, when the server is
-## # multitasked or multithreaded, it may receive a new HTTP request before the session is
-## # saved.
-## if self.session is not None and self.session.isDirty:
-## self.session.save()
-## if self.user is not None and self.user.isDirty:
-## self.user.save()
-
-## if isinstance(data, basestring):
-## dataFile = None
-## dataSize = len(data)
-## else:
-## dataFile = data
-## data = ''
-## if hasattr(dataFile, 'fileno'):
-## dataSize = os.fstat(dataFile.fileno())[6]
-## else:
-## # For StringIO and cStringIO classes.
-## dataSize = len(dataFile.getvalue())
-
-## if headers is None:
-## headers = {}
-## if time.time() > self.socketCreationTime + 300:
-## headers['Connection'] = 'close'
-## elif not self.close_connection:
-## headers['Connection'] = 'Keep-Alive'
-## if contentLocation is not None:
-## headers['Content-Location'] = contentLocation
-## if mimeType:
-## headers['Content-Type'] = '%s; charset=utf-8' % mimeType
-## if modificationTime:
-## headers['Last-Modified'] = time.strftime('%a, %d %b %Y %H:%M:%S GMT', modificationTime)
-## # TODO: Could also output Content-MD5.
-## ifModifiedSince = self.headers.get('If-Modified-Since')
-## if modificationTime and ifModifiedSince:
-## # We don't want to use bandwith if the file was not modified.
-## try:
-## ifModifiedSinceTime = time.strptime(ifModifiedSince[:25], '%a, %d %b %Y %H:%M:%S')
-## if modificationTime[:8] <= ifModifiedSinceTime[:8]:
-## self.send_response(304, 'Not Modified.')
-## for key in ('Connection', 'Content-Location'):
-## if key in headers:
-## self.send_header(key, headers[key])
-## self.setCookie()
-## self.end_headers()
-## return
-## except (ValueError, KeyError):
-## pass
-## if dataFile is not None:
-## assert not data
-## data = dataFile.read(1048576) # Read first MB chunk
-## if mimeType == 'text/html' and data.startswith('<?xml'):
-## # Internet Explorer 6 renders the page differently when they start with <?xml...>, so
-## # skip it.
-## i = data.find('\n')
-## if i > 0:
-## data = data[i + 1:]
-## else:
-## i = data.find('>')
-## if i > 0:
-## data = data[i + 1:]
-## dataSize -= i + 1
-## # Compress data if possible and if data is not too big.
-## acceptEncoding = self.headers.get('Accept-Encoding', '')
-## if 0 < dataSize < 1048576 and 'gzip' in acceptEncoding \
-## and 'gzip;q=0' not in acceptEncoding:
-## # Since dataSize < 1 MB, the data is fully contained in string.
-## zbuf = cStringIO.StringIO()
-## zfile = gzip.GzipFile(mode = 'wb', fileobj = zbuf)
-## zfile.write(data)
-## zfile.close()
-## data = zbuf.getvalue()
-## dataSize = len(data)
-## headers['Content-Encoding'] = 'gzip'
-## headers['Content-Length'] = '%d' % dataSize
-## successMessages = {
-## 200: 'OK',
-## 207: 'Multi-Status',
-## }
-## assert successCode in successMessages, 'Unknown success code %d.' % successCode
-## if self.httpAuthenticationLogoutTrick and successCode == 200:
-## successCode = 401
-## successMessage = 'Access Unauthorized'
-## headers['WWW-Authenticate'] = 'Basic realm="%s"' % self.realm
-## else:
-## successMessage = successMessages[successCode]
-## self.send_response(successCode, successMessage)
-## for key, value in headers.items():
-## self.send_header(key, value)
-## self.setCookie()
-## self.end_headers()
-## if self.httpRequest.method != 'HEAD' and dataSize > 0:
-## outputFile = self.wfile
-## if data:
-## outputFile.write(data)
-## if dataFile is not None:
-## while True:
-## chunk = dataFile.read(1048576) # 1 MB chunk
-## if not chunk:
-## break
-## outputFile.write(chunk)
-## return
-
-## def outputErrorAccessForbidden(self, filePath):
-## if filePath is None:
-## message = 'Access Forbidden'
-## else:
-## message = 'Access to "%s" Forbidden.' % filePath
-## logger.info(message)
-## data = '<html><body>%s</body></html>' % message
-## return self.send_error(403, message, data, setCookie = True)
-
-## def outputErrorBadRequest(self, reason):
-## if reason:
-## message = 'Bad Request: %s' % reason
-## else:
-## message = 'Bad Request'
-## logger.info(message)
-## data = '<html><body>%s</body></html>' % message
-## return self.send_error(400, message, data)
-
- def outputErrorInternalServer(self):
- message = 'Internal Server Error'
- logger.info(message)
- data = '<html><body>%s</body></html>' % message
- return self.send_error(500, message, data)
-
-## def outputErrorMethodNotAllowed(self, reason):
-## if reason:
-## message = 'Method Not Allowed: %s' % reason
-## else:
-## message = 'Method Not Allowed'
-## logger.info(message)
-## data = '<html><body>%s</body></html>' % message
-## # This error doesn't need a pretty interface.
-## # FIXME: Add an 'Allow' header containing a list of valid methods for the requested
-## # resource.
-## return self.send_error(405, message, data)
-
-## def outputErrorNotFound(self, filePath):
-## if filePath is None:
-## message = 'Not Found'
-## else:
-## message = 'Path "%s" Not Found.' % filePath
-## logger.info(message)
-## data = '<html><body>%s</body></html>' % message
-## return self.send_error(404, message, data, setCookie = True)
-
- def outputErrorUnauthorized(self, filePath):
- if filePath is None:
- message = 'Access Unauthorized'
- else:
- message = 'Access to "%s" Unauthorized.' % filePath
- logger.info(message)
- data = '<html><body>%s</body></html>' % message
- headers = {}
- if self.useHttpAuthentication:
- headers["WWW-Authenticate"] = 'Basic realm="%s"' % self.realm
- return self.send_error(401, message, data, headers, setCookie = True)
-
-## def outputInformationContinue(self):
-## message = 'Continue'
-## logger.debug(message)
-## self.send_response(100, message)
-
-## def outputSuccessCreated(self, filePath):
-## if filePath is None:
-## message = 'Created'
-## else:
-## message = 'File "%s" Created.' % filePath
-## logger.debug(message)
-## data = '<html><body>%s</body></html>' % message
-## self.send_response(201, message)
-## if time.time() > self.socketCreationTime + 300:
-## self.send_header('Connection', 'close')
-## elif not self.close_connection:
-## self.send_header('Connection', 'Keep-Alive')
-## self.send_header('Content-Type', 'text/html; charset=utf-8')
-## self.send_header('Content-Length', '%d' % len(data))
-## self.setCookie()
-## self.end_headers()
-## if self.httpRequest.method != 'HEAD':
-## self.wfile.write(data)
-
-## def outputSuccessNoContent(self):
-## message = 'No Content'
-## logger.debug(message)
-## self.send_response(204, message)
-## if time.time() > self.socketCreationTime + 300:
-## self.send_header('Connection', 'close')
-## elif not self.close_connection:
-## self.send_header('Connection', 'Keep-Alive')
-## self.setCookie()
-## self.end_headers()
-
- def outputUnknownException(self):
- import traceback, cStringIO
- f = cStringIO.StringIO()
- traceback.print_exc(file = f)
- exceptionTraceback = f.getvalue()
- exceptionType, exception = sys.exc_info()[:2]
- logger.debug("""\
-An exception "%(exception)s" of class "%(exceptionType)s" occurred.
-
-%(traceback)s
-""" % {
- 'exception': exception,
- 'exceptionType': exceptionType,
- 'traceback': exceptionTraceback,
- })
- return self.outputErrorInternalServer()
-
- def respondRedirectTemporarily(self, url):
- # Session and user must be saved before responding. Otherwise, when the server is
- # multitasked or multithreaded, it may receive a new HTTP request before the session is
- # saved.
- if self.session is not None and self.session.isDirty:
- self.session.save()
- if self.user is not None and self.user.isDirty:
- self.user.save()
-
- message = 'Moved Temporarily to "%s".' % url
- logger.debug(message)
- data = '<html><body>%s</body></html>' % message
- self.send_response(302, message)
- self.send_header('Location', url)
- if time.time() > self.socketCreationTime + 300:
- self.send_header('Connection', 'close')
- elif not self.close_connection:
- self.send_header('Connection', 'Keep-Alive')
- self.send_header('Content-Type', 'text/html; charset=utf-8')
- self.send_header('Content-Length', '%d' % len(data))
- self.setCookie()
- self.end_headers()
- if self.httpRequest.method != 'HEAD':
- self.wfile.write(data)
-
- def send_error(self, code, message = None, data = None, headers = None, setCookie = False):
- # Session and user must be saved before responding. Otherwise, when the server is
- # multitasked or multithreaded, it may receive a new HTTP request before the session is
- # saved.
- if self.session is not None and self.session.isDirty:
- self.session.save()
- if self.user is not None and self.user.isDirty:
- self.user.save()
-
- shortMessage, longMessage = self.responses.get(code, ('???', '???'))
- if message is None:
- message = shortMessage
- if not data:
- explain = longMessage
- data = self.error_message_format % {
- 'code': code,
- 'message': message,
- 'explain': longMessage,
- }
- self.send_response(code, message)
- self.send_header('Content-Type', 'text/html; charset=utf-8')
- self.send_header('Content-Length', '%d' % len(data))
- self.send_header('Connection', 'close')
- if headers is not None:
- for name, value in headers.items():
- self.send_header(name, value)
- if setCookie:
- self.setCookie()
- self.end_headers()
- if self.httpRequest.method != 'HEAD' and code >= 200 and code not in (204, 304):
- self.wfile.write(data)
-
- def setCookie(self):
- if not self.canUseCookie:
- return
- oldCookie = self.cookie
- cookie = Cookie.SimpleCookie()
- cookieContent = {}
- session = self.session
- if session is not None and session.publishToken:
- cookieContent['sessionToken'] = session.token
- for key, value in cookieContent.items():
- cookie[key] = value
- cookie[key]['path'] = '/'
- if not cookieContent:
- if oldCookie:
- for key, morsel in oldCookie.items():
- cookie[key] = ''
- cookie[key]['max-age'] = 0
- cookie[key]['path'] = '/'
- else:
- cookie = None
- if cookie is not None:
- # Is new cookie different from previous one?
- sameCookie = False
- if oldCookie is not None and cookie.keys() == oldCookie.keys():
- for key, morsel in cookie.items():
- oldMorsel = oldCookie[key]
- if morsel.value != oldMorsel.value:
- break
- else:
- sameCookie = True
- if not sameCookie:
- for morsel in cookie.values():
- self.send_header(
- 'Set-Cookie', morsel.output(header = '')[1:])
- self.cookie = cookie
-
-
-class HttpRequestHandler(HttpRequestHandlerMixin, BaseHTTPServer.BaseHTTPRequestHandler):
- scheme = 'http'
-
-
-class HttpsConnection(httplib.HTTPConnection):
- certificateFile = None
- default_port = httplib.HTTPS_PORT
- peerCaCertificateFile = None
- privateKeyFile = None
-
- def __init__(self, host, port = None, privateKeyFile = None, certificateFile = None,
- peerCaCertificateFile = None, strict = None):
- httplib.HTTPConnection.__init__(self, host, port, strict)
- self.privateKeyFile = privateKeyFile
- self.certificateFile = certificateFile
- self.peerCaCertificateFile = peerCaCertificateFile
-
- def connect(self):
- """Connect to a host on a given (SSL) port."""
-
- context = SSL.Context(SSL.SSLv23_METHOD)
- # Demand a certificate.
- context.set_verify(SSL.VERIFY_PEER | SSL.VERIFY_FAIL_IF_NO_PEER_CERT, self.verifyCallback)
- if self.privateKeyFile:
- context.use_privatekey_file(self.privateKeyFile)
- if self.certificateFile:
- context.use_certificate_file(self.certificateFile)
- if self.peerCaCertificateFile:
- context.load_verify_locations(self.peerCaCertificateFile)
-
- # Strange hack, that is derivated from httplib.HTTPSConnection, but that I (Emmanuel) don't
- # really understand...
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- sslSocket = SSL.Connection(context, sock)
- sslSocket.connect((self.host, self.port))
- self.sock = httplib.FakeSocket(sslSocket, sslSocket)
-
- def verifyCallback(self, connection, x509Object, errorNumber, errorDepth, returnCode):
- logger.debug('http.HttpsConnection(%s, %s, %s, %s, %s, %s)' % (
- self, connection, x509Object, errorNumber, errorDepth, returnCode))
- # FIXME: What should be done?
- return returnCode
-
-
-class HttpsRequestHandler(HttpRequestHandlerMixin, BaseHTTPSRequestHandler):
- scheme = 'https'
-
-
-## # We use ForkingMixIn instead of ThreadingMixIn because the Python binding for
-## # libxml2 limits the number of registered xpath functions to 10. Even if we use
-## # only one xpathContext, this would limit the number of threads to 10, wich is
-## # not enough for a web server.
-
-
-## class HttpServer(SocketServer.ForkingMixIn, BaseHTTPServer.HTTPServer):
-## pass
-
-
-## class HttpsServer(SocketServer.ForkingMixIn, BaseHTTPSServer):
-## pass
-
-
-# No fork nor thread.
-
-class HttpServer(BaseHTTPServer.HTTPServer):
- pass
-
-
-class HttpsServer(BaseHTTPSServer):
- pass
-