This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
expression/src/core/http.py

799 lines
32 KiB
Python

# -*- coding: UTF-8 -*-
# Expression
# By: Frederic Peters <fpeters@entrouvert.com>
# Emmanuel Raviart <eraviart@entrouvert.com>
#
# Copyright (C) 2004 Entr'ouvert, Frederic Peters & Emmanuel Raviart
#
# 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 & HTTPS Module"""
import BaseHTTPServer
import Cookie
import cStringIO
import gzip
import httplib
import logging
import os
import random
import socket
import SocketServer
import sys
import time
try:
from OpenSSL import SSL
except ImportError:
SSL = None
import environs
import expression
import logs
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):
randomCharacters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
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)
# Needed for the clients to be able to reuse the same connection.
# Without this, we got OpenSSL.SSL.Error:
# [('SSL routines', 'SSL_GET_PREV_SESSION', 'session id context uninitialized')]
sessionId = ''.join([random.choice(self.randomCharacters) for i in range(16)])
ctx.set_session_id(sessionId)
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):
logs.info("http.BaseHTTPSServer.verifyCallback(%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 HttpRequestHandlerMixin:
application = None # Class variable.
baseEnviron = None # Class variable.
socketCreationTime = None
protocol_version = "HTTP/1.1"
server_version = "Expression/%s" % expression.versionNumber
sys_version = "Python/%s" % sys.version.split()[0]
def doHttpCommand(self):
# Initialize virtual server logger.
loggerName = self.headers.get("Host", "").replace(".", "_")
logger = logging.getLogger(loggerName)
logger.info(self.raw_requestline.strip())
logger.debug(str(self.headers))
environs.initFromOther(
self.baseEnviron,
_level = "doHttpCommand",
logger = logger,
)
environ = environs.get()
if hasattr(self.connection, "get_peer_certificate"):
cert = self.connection.get_peer_certificate()
environs.setVar("peerCertificate", cert)
try:
self.application.handleHttpCommand(self)
except IOError:
logger.exception("An exception occured:")
path = self.path.split("?")[0]
return self.outputErrorNotFound(path)
assert environ == environs.get()
do_DELETE = doHttpCommand
do_GET = doHttpCommand
do_HEAD = doHttpCommand
do_MKCOL = doHttpCommand
do_OPTIONS = doHttpCommand
do_POST = doHttpCommand
do_PROPFIND = doHttpCommand
do_PUT = doHttpCommand
def handle(self):
self.socketCreationTime = time.time()
try:
try:
self.__class__.__bases__[1].handle(self)
except socket.timeout:
pass
except KeyboardInterrupt:
raise
except SSL.ZeroReturnError:
pass
except SSL.Error, exception:
raise str((exception, exception[0]))
if exception[0] == (
"PEM routines", "PEM_read_bio", "no start line"):
pass
else:
self.outputUnknownException()
except:
self.outputUnknownException()
finally:
del self.socketCreationTime
def log_date_time_string(self):
return time.strftime("%d/%b/%Y:%H:%M:%S %z")
def log_message(self, format, *args):
"""Override HttpRequestHandler method to use logging module."""
logs.info("%s - - [%s ] %s" % (
self.address_string(),
self.log_date_time_string(),
format % args))
virtualHost = environs.getVar("virtualHost", default = None)
if virtualHost and virtualHost.accessLogFile:
referer = self.headers.get("Referer", "-")
if referer == "BAD":
return
useragent = self.headers.get("User-agent", "-")
virtualHost.accessLogFile.write("%s - - [%s] %s \"%s\" \"%s\"\n" %
(self.address_string(), self.log_date_time_string(), format%args,
referer, useragent))
def log_request(self, code = "-", size = "-"):
if size == "-":
try:
size = self.response_headers["Content-Length"]
except (AttributeError, KeyError):
pass
self.log_message('"%s" %s %s', self.requestline, str(code), str(size))
def outputAlert(self, data, station = "current", title = None, url = None):
import html
if title is None:
title = N_("Alert")
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.div(
html.label(_(title)),
html.p(_(data), class_ = "alert"),
buttonsBar,
class_ = "xforms-group"
)
)
)
xmlDocument = layout.generateXmlDocument()
if station == "current":
station = environs.getVar("currentStation", default = None)
if station:
xslt = station.getDataHolder().getSiteXslt()
mimeType, styledData = station.generateXmlDocumentStyledData(xmlDocument, xslt)
if mimeType == "text/html":
data = styledData
else:
data = xmlDocument.serialize()
self.outputData(data, contentLocation = None, mimeType = "text/html",
station = station)
def outputData(self, data, contentLocation = "current", headers = None, mimeType = None,
modificationTime = None, charset = None, station = "current", successCode = 200):
if station == "current":
station = environs.getVar("currentStation", default = None)
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())
# We need to save the session before sending response, otherwise, the
# server may receive a new HTTP request before the session is saved.
session = environs.getVar("session")
if session is not None:
sessionDocument = session.getDocument()
if sessionDocument.isDirty:
sessionDocument.save()
if headers is None:
headers = {}
self.response_headers = headers
if time.time() > self.socketCreationTime + 300:
headers["Connection"] = "close"
elif not self.close_connection:
headers["Connection"] = "Keep-Alive"
if contentLocation == "current":
contentLocation = station.getUriAbsolutePath()
if contentLocation is not None:
headers["Content-Location"] = contentLocation
if mimeType:
if not mimeType.startswith('text/'):
headers["Content-Type"] = mimeType
else:
if not charset:
charset = "utf-8" # default to utf-8
headers["Content-Type"] = "%s; charset=%s" % (mimeType, charset)
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 environs.getVar("httpAuthenticationLogoutTrick", default = False) \
and successCode == 200:
successCode = 401
successMessage = "Access Unauthorized"
headers["WWW-Authenticate"] = 'Basic realm="%s"' % station.getConfigString(
"yep:title", default = "Expression")
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.command != "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, station = "current"):
if filePath is None:
message = "Access Forbidden"
else:
message = """Access to "%s" Forbidden.""" % filePath
if station == "current":
station = environs.getVar("currentStation", default = None)
logs.info(message)
data = "<html><body>%s</body></html>" % message
if station:
import httpstatuses
errorElement = httpstatuses.Http403.newInTemporaryDocument(message, filePath)
errorDescription = errorElement.getDescription()
if errorDescription:
xmlDocument = errorDescription.generateXmlDocument()
xslt = station.getDataHolder().getSiteXslt()
mimeType, styledData = station.generateXmlDocumentStyledData(xmlDocument, xslt)
if mimeType == "text/html":
data = styledData
return self.send_error(403, message, data, setCookie = True)
def outputErrorBadRequest(self, reason, station = "current"):
if reason:
message = "Bad Request: %s" % reason
else:
message = "Bad Request"
if station == "current":
station = environs.getVar("currentStation", default = None)
logs.info(message)
data = "<html><body>%s</body></html>" % message
if station:
import httpstatuses
errorElement = httpstatuses.Http400.newInTemporaryDocument(message)
errorDescription = errorElement.getDescription()
if errorDescription:
xmlDocument = errorDescription.generateXmlDocument()
xslt = station.getDataHolder().getSiteXslt()
mimeType, styledData = station.generateXmlDocumentStyledData(xmlDocument, xslt)
if mimeType == "text/html":
data = styledData
return self.send_error(400, message, data)
def outputErrorInternalServer(self, station = "current"):
if station == "current":
station = environs.getVar("currentStation", default = None)
message = "Internal Server Error"
logs.info(message)
data = "<html><body>%s</body></html>" % message
if station:
import httpstatuses
errorElement = httpstatuses.Http500.newInTemporaryDocument(message)
errorDescription = errorElement.getDescription()
if errorDescription:
xmlDocument = errorDescription.generateXmlDocument()
xslt = station.getDataHolder().getSiteXslt()
mimeType, styledData = station.generateXmlDocumentStyledData(xmlDocument, xslt)
if mimeType == "text/html":
data = styledData
return self.send_error(500, message, data)
def outputErrorMethodNotAllowed(self, reason, station = "current"):
if reason:
message = "Method Not Allowed: %s" % reason
else:
message = "Method Not Allowed"
if station == "current":
station = environs.getVar("currentStation", default = None)
logs.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, station = "current"):
if filePath is None:
message = "Not Found"
else:
message = """Path "%s" Not Found.""" % filePath
if station == "current":
station = environs.getVar("currentStation", default = None)
logs.info(message)
data = "<html><body>%s</body></html>" % message
if station:
import httpstatuses
errorElement = httpstatuses.Http404.newInTemporaryDocument(message, filePath)
errorDescription = errorElement.getDescription()
if errorDescription:
xmlDocument = errorDescription.generateXmlDocument()
xslt = station.getDataHolder().getSiteXslt()
mimeType, styledData = station.generateXmlDocumentStyledData(xmlDocument, xslt)
if mimeType == "text/html":
data = styledData
return self.send_error(404, message, data, setCookie = True)
def outputErrorUnauthorized(self, filePath, station = "current"):
if filePath is None:
message = "Access Unauthorized"
else:
message = """Access to "%s" Unauthorized.""" % filePath
if station == "current":
station = environs.getVar("currentStation", default = None)
logs.info(message)
data = "<html><body>%s</body></html>" % message
headers = {}
if station:
import httpstatuses
errorElement = httpstatuses.Http401.newInTemporaryDocument(message, filePath)
errorDescription = errorElement.getDescription()
if errorDescription:
xmlDocument = errorDescription.generateXmlDocument()
xslt = station.getDataHolder().getSiteXslt()
mimeType, styledData = station.generateXmlDocumentStyledData(xmlDocument, xslt)
if mimeType == "text/html":
data = styledData
if station.getConfigBoolean("yep:useHttpAuthentication", default = False):
headers["WWW-Authenticate"] = 'Basic realm="%s"' % station.getConfigString(
"yep:title", default = "Expression")
return self.send_error(401, message, data, headers, setCookie = True)
def outputInformationContinue(self):
message = "Continue"
logs.debug(message)
self.send_response(100, message)
def outputRedirect(self, uri, station = "current"):
assert uri
if station == "current":
station = environs.getVar("currentStation", default = None)
# We need to save the session before sending response, otherwise, the
# server may receive a new HTTP request before the session is saved.
session = environs.getVar("session")
if session is not None:
sessionDocument = session.getDocument()
if sessionDocument.isDirty:
sessionDocument.save()
message = """Moved Temporarily to "%s".""" % uri
logs.debug(message)
data = "<html><body>%s</body></html>" % message
if station:
import httpstatuses
errorElement = httpstatuses.Http302.newInTemporaryDocument(message, uri)
errorDescription = errorElement.getDescription()
if errorDescription:
xmlDocument = errorDescription.generateXmlDocument()
xslt = station.getDataHolder().getSiteXslt()
mimeType, styledData = station.generateXmlDocumentStyledData(xmlDocument, xslt)
if mimeType == "text/html":
data = styledData
self.send_response(302, message)
self.send_header("Location", uri)
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.command != "HEAD":
self.wfile.write(data)
def outputRedirectPermanent(self, uri, station = "current"):
assert uri
if station == "current":
station = environs.getVar("currentStation", default = None)
# We need to save the session before sending response, otherwise, the
# server may receive a new HTTP request before the session is saved.
session = environs.getVar("session")
if session is not None:
sessionDocument = session.getDocument()
if sessionDocument.isDirty:
sessionDocument.save()
message = """Moved Permanently to "%s".""" % uri
logs.debug(message)
data = "<html><body>%s</body></html>" % message
if station:
import httpstatuses
errorElement = httpstatuses.Http301.newInTemporaryDocument(message, uri)
errorDescription = errorElement.getDescription()
if errorDescription:
xmlDocument = errorDescription.generateXmlDocument()
xslt = station.getDataHolder().getSiteXslt()
mimeType, styledData = station.generateXmlDocumentStyledData(xmlDocument, xslt)
if mimeType == "text/html":
data = styledData
self.send_response(301, message)
self.send_header("Location", uri)
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.command != "HEAD":
self.wfile.write(data)
def outputSuccessCreated(self, filePath, station = "current"):
if filePath is None:
message = "Created"
else:
message = """File "%s" Created.""" % filePath
if station == "current":
station = environs.getVar("currentStation", default = None)
logs.debug(message)
data = "<html><body>%s</body></html>" % message
if station:
import httpstatuses
errorElement = httpstatuses.Http201.newInTemporaryDocument(message, filePath)
errorDescription = errorElement.getDescription()
if errorDescription:
xmlDocument = errorDescription.generateXmlDocument()
xslt = station.getDataHolder().getSiteXslt()
mimeType, styledData = station.generateXmlDocumentStyledData(xmlDocument, xslt)
if mimeType == "text/html":
data = styledData
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.command != "HEAD":
self.wfile.write(data)
def outputSuccessNoContent(self):
message = "No Content"
logs.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]
virtualHost = environs.getVar("virtualHost", default = None)
if environs.getVar("debug"):
logs.debug("""\
An exception "%(exception)s" of class "%(exceptionType)s" occurred.
%(traceback)s
""" % {
"exception": exception,
"exceptionType": exceptionType,
"traceback": exceptionTraceback,
})
if virtualHost and virtualHost.getConfigExistence("yep:talkback") \
and environs.getVar("sendEmails"):
try:
userAgent = self.headers["User-Agent"]
except KeyError:
userAgent = None
try:
host = self.headers["Host"]
except:
host = "%s (from socket.getfqdn())" % socket.getfqdn()
adminEmailAddress = virtualHost.getConfigString(
"yep:adminEmail", default = "root@localhost")
message = """\
From: %(from)s
To: %(to)s
Subject: [%(host)s] Expression Talkback
An exception "%(exception)s" of class "%(exceptionType)s" occurred.
Url: %(uriScheme)s://%(host)s%(url)s
User-Agent: %(userAgent)s
Client: %(clientIp)s
%(traceback)s
""" % {
"clientIp": self.request.getpeername()[0],
"exception": exception,
"exceptionType": exceptionType,
"from": adminEmailAddress,
"host": host,
"to": adminEmailAddress,
"traceback": exceptionTraceback,
"uriScheme": environs.getVar("httpServer").uriScheme,
"url": self.path,
"userAgent": userAgent,
}
import smtplib
server = smtplib.SMTP("localhost")
try:
server.sendmail(adminEmailAddress, adminEmailAddress, message)
except smtplib.SMTPException, error:
logs.warning("SMTP error while sending talkback: %r" % error)
return self.outputErrorInternalServer()
def send_error(self, code, message = None, data = None, headers = None, setCookie = False):
# We need to save the session before sending response, otherwise, the
# server may receive a new HTTP request before the session is saved.
session = environs.getVar("session", default = None)
if session is not None:
sessionDocument = session.getDocument()
if sessionDocument.isDirty:
sessionDocument.save()
if not data:
return self.__class__.__bases__[1].send_error(self, code, message)
shortMessage, longMessage = self.responses[code]
if message is None:
message = shortMessage
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.command != "HEAD":
self.wfile.write(data)
def setCookie(self):
if not environs.getVar("canUseCookie", default = False):
return
oldCookie = environs.getVar("cookie")
cookie = Cookie.SimpleCookie()
cookieContent = {}
session = environs.getVar("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:])
environs.get(_level = "handleHttpCommand").setVar(
"cookie", cookie)
def version_string(self):
"""Return the server software version string."""
return "Expression %s" % expression.versionNumber
class HttpRequestHandler(HttpRequestHandlerMixin,
BaseHTTPServer.BaseHTTPRequestHandler):
pass
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)
if not self.peerCaCertificateFile:
context.set_verify(SSL.VERIFY_NONE, self.verifyCallback)
else:
# 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):
logs.debug("http.HttpsConnection.verifyCallback(%s, %s, %s, %s, %s, %s)" % (
self, connection, x509Object, errorNumber, errorDepth, returnCode))
# FIXME: What should be done?
return returnCode
class HttpsRequestHandler(HttpRequestHandlerMixin, BaseHTTPSRequestHandler):
pass
# 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):
application = None
uriScheme = "http"
class HttpsServer(SocketServer.ForkingMixIn, BaseHTTPSServer):
application = None
uriScheme = "https"