Commit 8e52f973 authored by Hanno Schlichting's avatar Hanno Schlichting

Add new `Zope2.Startup.config` module to hold configuration.

This avoids various import dependencies between the Startup code
and ZServer/webdav.
parent eec9a477
......@@ -21,6 +21,8 @@ Features Added
Restructuring
+++++++++++++
- Add new `Zope2.Startup.config` module to hold configuration.
- Remove `Control_Panel` `/DebugInfo` and `/DavLocks`.
- Remove profiling support via `publisher-profile-file` directive.
......
......@@ -28,8 +28,8 @@ def shutdown(exit_code,fast = 0):
global _shutdown_timeout
if _shutdown_phase == 0:
# Thread safety? proably no need to care
import ZServer
ZServer.exit_code = exit_code
from Zope2.Startup import config
config.ZSERVER_EXIT_CODE = exit_code
_shutdown_phase = 1
if fast:
# Someone wants us to shutdown fast. This is hooked into SIGTERM - so
......
......@@ -23,7 +23,7 @@ dummyLOG = StringIO()
def setNumberOfThreads(number_of_threads):
'''Sets number of ZServer threads.'''
try:
from ZServer.PubCore import setNumberOfThreads
from Zope2.Startup.config import setNumberOfThreads
setNumberOfThreads(number_of_threads)
except ImportError:
pass
......
......@@ -560,12 +560,13 @@ class HTTPResponse(BaseResponse):
# The following two methods are part of a private protocol with the
# publisher for handling fatal import errors and TTW shutdown requests.
_shutdown_flag = None
def _requestShutdown(self, exitCode=0):
""" Request that the server shut down with exitCode after fulfilling
the current request.
"""
import ZServer
ZServer.exit_code = exitCode
from Zope2.Startup import config
config.ZSERVER_EXIT_CODE = exitCode
self._shutdown_flag = 1
def _shutdownRequested(self):
......@@ -573,7 +574,6 @@ class HTTPResponse(BaseResponse):
"""
return self._shutdown_flag is not None
def _encode_unicode(self,body,
charset_re=re.compile(
r'(?:application|text)/[-+0-9a-z]+\s*;\s*' +
......
......@@ -88,10 +88,10 @@ def get_http_header_max_length():
class zhttp_collector:
def __init__(self, handler, request, size):
from ZServer import LARGE_FILE_THRESHOLD
from Zope2.Startup.config import ZSERVER_LARGE_FILE_THRESHOLD
self.handler = handler
self.request = request
if size > LARGE_FILE_THRESHOLD:
if size > ZSERVER_LARGE_FILE_THRESHOLD:
# write large upload data to a file
from tempfile import TemporaryFile
self.data = TemporaryFile('w+b')
......
......@@ -44,6 +44,8 @@ from ZPublisher.HTTPRequest import HTTPRequest
from Producers import ShutdownProducer, LoggingProducer, CallbackProducer
import DebugLogger
from Zope2.Startup import config
from cStringIO import StringIO
from tempfile import TemporaryFile
import socket, string, os, sys, time
......@@ -388,9 +390,11 @@ class PCGIPipe:
lambda t=('E', id(self._channel)): apply(DebugLogger.log,t)), 0)
if self._shutdown:
try: r=self._shutdown[0]
except: r=0
ZServer.exit_code=r
try:
r = self._shutdown[0]
except:
r = 0
config.ZSERVER_EXIT_CODE = r
self._channel.push(ShutdownProducer(), 0)
Wakeup(lambda: asyncore.close_all())
else:
......
......@@ -11,22 +11,19 @@
#
##############################################################################
import ZRendezvous
from Zope2.Startup.config import ( # NOQA
setNumberOfThreads,
ZSERVER_THREADS as _n,
)
from ZServer.PubCore import ZRendezvous
_handle = None
_handle=None
_n=1
def handle(*args, **kw):
global _handle
if _handle is None: _handle=ZRendezvous.ZRendevous(_n).handle
return apply(_handle, args, kw)
if _handle is None:
_handle = ZRendezvous.ZRendevous(_n).handle
def setNumberOfThreads(n):
"""This function will self-destruct in 4 statements.
"""
global _n
_n=n
global setNumberOfThreads
del setNumberOfThreads
return _handle(*args, **kw)
......@@ -11,26 +11,18 @@
#
##############################################################################
import sys
import utils
#########################################################
### declarations used by external packages
# the exit code used to exit a Zope process cleanly
exit_code = 0
from Zope2.Startup.config import ( # NOQA
ZSERVER_CONNECTION_LIMIT as CONNECTION_LIMIT,
ZSERVER_EXIT_CODE as exit_code,
ZSERVER_LARGE_FILE_THRESHOLD as LARGE_FILE_THRESHOLD,
setNumberOfThreads,
)
# the ZServer version number
ZSERVER_VERSION = '1.1'
# the maximum number of incoming connections to ZServer
CONNECTION_LIMIT = 1000 # may be reset by max_listen_sockets handler in Zope
# request bigger than this size get saved into a
# temporary file instead of being read completely into memory
LARGE_FILE_THRESHOLD = 1 << 19 # may be reset by large_file_threshold
# handler in Zope
# the Zope version string
ZOPE_VERSION = utils.getZopeVersion()
......@@ -42,17 +34,11 @@ from HTTPServer import zhttp_server, zhttp_handler
from PCGIServer import PCGIServer
from FCGIServer import FCGIServer
from FTPServer import FTPServer
from PubCore import setNumberOfThreads
from medusa.monitor import secure_monitor_server
### end declarations
##########################################################
# we need to patch asyncore's dispatcher class with a new
# log_info method so we see medusa messages in the zLOG log
utils.patchAsyncoreLogger()
# we need to patch the 'service name' of the medusa syslog logger
utils.patchSyslogServiceName()
......@@ -48,7 +48,8 @@ from urllib import unquote
# The ZConfig machinery may sets this attribute on initialization
# if any trusted-proxies
trusted_proxies = []
from Zope2.Startup.config import TRUSTED_PROXIES as trusted_proxies # NOQA
class http_request:
......
......@@ -26,6 +26,8 @@ from ZConfig.components.logger import loghandler
from zope.event import notify
from zope.processlifetime import ProcessStarting
import Zope2.Startup.config
try:
IO_ERRORS = (IOError, WindowsError)
except NameError:
......@@ -35,6 +37,7 @@ except NameError:
logger = logging.getLogger("Zope")
started = False
def get_starter():
if sys.platform[:3].lower() == "win":
return WindowsZopeStarter()
......@@ -105,10 +108,10 @@ class ZopeStarter:
try:
from App.config import getConfiguration
config = getConfiguration()
import ZServer
import Lifetime
Lifetime.loop()
sys.exit(ZServer.exit_code)
from Zope2.Startup.config import ZSERVER_EXIT_CODE
sys.exit(ZSERVER_EXIT_CODE)
finally:
self.shutdown()
......@@ -137,10 +140,11 @@ class ZopeStarter:
ZPublisher.Publish.set_default_authentication_realm(
self.cfg.http_realm)
if self.cfg.trusted_proxies:
# DM 2004-11-24: added host name mapping (such that examples in conf file really have a chance to work
mapped = []
for name in self.cfg.trusted_proxies: mapped.extend(_name2Ips(name))
for name in self.cfg.trusted_proxies:
mapped.extend(_name2Ips(name))
ZPublisher.HTTPRequest.trusted_proxies = tuple(mapped)
Zope2.Startup.config.TRUSTED_PROXIES = tuple(mapped)
def setupSecurityOptions(self):
import AccessControl
......@@ -183,9 +187,9 @@ class ZopeStarter:
def setupZServer(self):
# Increase the number of threads
import ZServer
ZServer.setNumberOfThreads(self.cfg.zserver_threads)
ZServer.CONNECTION_LIMIT = self.cfg.max_listen_sockets
Zope2.Startup.config.setNumberOfThreads(self.cfg.zserver_threads)
Zope2.Startup.config.ZSERVER_CONNECTION_LIMIT = \
self.cfg.max_listen_sockets
def serverListen(self):
for server in self.cfg.servers:
......
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
TRUSTED_PROXIES = []
ZSERVER_CONNECTION_LIMIT = 1000
ZSERVER_ENABLE_MS_PUBLIC_HEADER = False
ZSERVER_EXIT_CODE = 0
ZSERVER_LARGE_FILE_THRESHOLD = 524288
ZSERVER_THREADS = 1
def setNumberOfThreads(n):
"""This function will self-destruct in 4 statements.
"""
global ZSERVER_THREADS
ZSERVER_THREADS = n
global setNumberOfThreads
del setNumberOfThreads
import os
import re
import sys
from re import compile
from socket import gethostbyaddr
# top-level key handlers
from Zope2.Startup import config
# XXX I suspect there are still problems here. In most cases, if the
# value is not set from the config file, or if the value is the
# default, the corresponding envvar is not unset, which is needed to
# ensure that the environment variables and the configuration values
# reflect a coherant configuration.
def _setenv(name, value):
if isinstance(value, str):
os.environ[name] = value
else:
os.environ[name] = `value`
os.environ[name] = repr(value)
def debug_mode(value):
value and _setenv('Z_DEBUG_MODE', '1')
return value
def locale(value):
import locale
locale.setlocale(locale.LC_ALL, value)
return value
def datetime_format(value):
value and _setenv('DATETIME_FORMAT', value)
return value
def automatically_quote_dtml_request_data(value):
not value and _setenv('ZOPE_DTML_REQUEST_AUTOQUOTE', '0')
return value
def maximum_number_of_session_objects(value):
default = 1000
value not in (None, default) and _setenv('ZSESSION_OBJECT_LIMIT', value)
return value
def session_add_notify_script_path(value):
value is not None and _setenv('ZSESSION_ADD_NOTIFY', value)
return value
def session_delete_notify_script_path(value):
value is not None and _setenv('ZSESSION_DEL_NOTIFY', value)
return value
def session_timeout_minutes(value):
default = 20
value not in (None, default) and _setenv('ZSESSION_TIMEOUT_MINS', value)
return value
def large_file_threshold(value):
import ZServer
ZServer.LARGE_FILE_THRESHOLD = value
config.ZSERVER_LARGE_FILE_THRESHOLD = value
def http_realm(value):
value is not None and _setenv('Z_REALM', value)
return value
def max_listen_sockets(value):
import ZServer
ZServer.CONNECTION_LIMIT = value
config.ZSERVER_CONNECTION_LIMIT = value
def cgi_maxlen(value):
import cgi
cgi.maxlen = value
def http_header_max_length(value):
return value
def enable_ms_public_header(value):
import webdav
webdav.enable_ms_public_header = value
config.ZSERVER_ENABLE_MS_PUBLIC_HEADER = value
# server handlers
def root_handler(config):
def root_handler(cfg):
""" Mutate the configuration with defaults and perform
fixups of values that require knowledge about configuration
values outside of their context.
"""
# Set environment variables
for k,v in config.environment.items():
for k, v in cfg.environment.items():
os.environ[k] = v
# Add directories to the pythonpath
instancelib = os.path.join(config.instancehome, 'lib', 'python')
if instancelib not in config.path:
instancelib = os.path.join(cfg.instancehome, 'lib', 'python')
if instancelib not in cfg.path:
if os.path.isdir(instancelib):
config.path.append(instancelib)
path = config.path[:]
cfg.path.append(instancelib)
path = cfg.path[:]
path.reverse()
for dir in path:
sys.path.insert(0, dir)
......@@ -101,56 +105,55 @@ def root_handler(config):
# Add any product directories not already in Products.__path__.
# Directories are added in the order they are mentioned
instanceprod = os.path.join(config.instancehome, 'Products')
if instanceprod not in config.products:
instanceprod = os.path.join(cfg.instancehome, 'Products')
if instanceprod not in cfg.products:
if os.path.isdir(instanceprod):
config.products.append(instanceprod)
cfg.products.append(instanceprod)
import Products
L = []
for d in config.products + Products.__path__:
for d in cfg.products + Products.__path__:
if d not in L:
L.append(d)
Products.__path__[:] = L
# if no servers are defined, create default http server and ftp server
if not config.servers:
config.servers = []
if not cfg.servers:
cfg.servers = []
# prepare servers:
for factory in config.servers:
factory.prepare(config.ip_address or '',
config.dns_resolver,
for factory in cfg.servers:
factory.prepare(cfg.ip_address or '',
cfg.dns_resolver,
"Zope2",
config.cgi_environment,
config.port_base)
cfg.cgi_environment,
cfg.port_base)
# set up trusted proxies
if config.trusted_proxies:
from ZPublisher import HTTPRequest
from ZServer.medusa import http_server
# DM 2004-11-24: added host name mapping (such that examples in
# conf file really have a chance to work
if cfg.trusted_proxies:
mapped = []
for name in config.trusted_proxies: mapped.extend(_name2Ips(name))
for name in cfg.trusted_proxies:
mapped.extend(_name2Ips(name))
config.TRUSTED_PROXIES = tuple(mapped)
from ZPublisher import HTTPRequest
HTTPRequest.trusted_proxies = tuple(mapped)
http_server.trusted_proxies = tuple(mapped)
# set the maximum number of ConflictError retries
if config.max_conflict_retries:
if cfg.max_conflict_retries:
from ZPublisher import HTTPRequest
HTTPRequest.retry_max_count = config.max_conflict_retries
HTTPRequest.retry_max_count = cfg.max_conflict_retries
def handleConfig(config, multihandler):
def handleConfig(cfg, multihandler):
handlers = {}
for name, value in globals().items():
if not name.startswith('_'):
handlers[name] = value
return multihandler(handlers)
# DM 2004-11-24: added
def _name2Ips(host, isIp_=compile(r'(\d+\.){3}').match):
def _name2Ips(host, isIp_=re.compile(r'(\d+\.){3}').match):
"""Map a name *host* to the sequence of its ip addresses.
use *host* itself (as sequence) if it already is an ip address.
......
......@@ -179,8 +179,8 @@ class ZopeStarterTestCase(LoggingTestHelper, unittest.TestCase):
zserver-threads 10""")
starter = self.get_starter(conf)
starter.setupZServer()
from ZServer.PubCore import _n
self.assertEqual(_n, 10)
from Zope2.Startup.config import ZSERVER_THREADS
self.assertEqual(ZSERVER_THREADS, 10)
def testSetupServers(self):
# We generate a random port number to test against, so that multiple
......
......@@ -105,26 +105,26 @@ class StartupTestCase(unittest.TestCase):
self.assertEqual(items, [("FEARFACTORY", "rocks"), ("NSYNC","doesnt")])
def test_ms_public_header(self):
import webdav
from Zope2.Startup import config
from Zope2.Startup.handlers import handleConfig
default_setting = webdav.enable_ms_public_header
default_setting = config.ZSERVER_ENABLE_MS_PUBLIC_HEADER
try:
conf, handler = self.load_config_text("""\
instancehome <<INSTANCE_HOME>>
enable-ms-public-header true
""")
handleConfig(None, handler)
self.assert_(webdav.enable_ms_public_header == True)
self.assertTrue(config.ZSERVER_ENABLE_MS_PUBLIC_HEADER)
conf, handler = self.load_config_text("""\
instancehome <<INSTANCE_HOME>>
enable-ms-public-header false
""")
handleConfig(None, handler)
self.assert_(webdav.enable_ms_public_header == False)
self.assertFalse(config.ZSERVER_ENABLE_MS_PUBLIC_HEADER)
finally:
webdav.enable_ms_public_header = default_setting
config.ZSERVER_ENABLE_MS_PUBLIC_HEADER = default_setting
def test_path(self):
p1 = tempfile.mktemp()
......
......@@ -105,7 +105,7 @@ class NullResource(Persistent, Implicit, Resource):
def PUT(self, REQUEST, RESPONSE):
"""Create a new non-collection resource.
"""
from ZServer import LARGE_FILE_THRESHOLD
from Zope2.Startup.config import ZSERVER_LARGE_FILE_THRESHOLD
self.dav__init(REQUEST, RESPONSE)
......@@ -125,8 +125,9 @@ class NullResource(Persistent, Implicit, Resource):
raise PreconditionFailed
# SDS: Only use BODY if the file size is smaller than
# LARGE_FILE_THRESHOLD, otherwise read LARGE_FILE_THRESHOLD
# bytes from the file which should be enough to trigger
# ZSERVER_LARGE_FILE_THRESHOLD, otherwise read
# ZSERVER_LARGE_FILE_THRESHOLD bytes from the file
# which should be enough to trigger
# content_type detection, and possibly enough for CMF's
# content_type_registry too.
#
......@@ -142,7 +143,8 @@ class NullResource(Persistent, Implicit, Resource):
# REQUEST['BODYFILE'] directly and try as much as possible not
# to read the whole file into memory.
if int(REQUEST.get('CONTENT_LENGTH') or 0) > LARGE_FILE_THRESHOLD:
if (int(REQUEST.get('CONTENT_LENGTH') or 0) >
ZSERVER_LARGE_FILE_THRESHOLD):
file = REQUEST['BODYFILE']
body = file.read(LARGE_FILE_THRESHOLD)
file.seek(0)
......
......@@ -40,6 +40,7 @@ from zExceptions import Forbidden
from zExceptions import MethodNotAllowed
from zExceptions import NotFound
from zExceptions import Unauthorized
import Zope2.Startup.config
from ZPublisher.HTTPRangeSupport import HTTPRangeInterface
from zope.interface import implements
......@@ -235,7 +236,7 @@ class Resource(Base, LockableItem):
# Microsoft Web Folders compatibility, only enabled if
# User-Agent matches.
if ms_dav_agent.match(REQUEST.get_header('User-Agent', '')):
if webdav.enable_ms_public_header:
if Zope2.Startup.config.ZSERVER_ENABLE_MS_PUBLIC_HEADER:
RESPONSE.setHeader('Public', ', '.join(self.__http_methods__))
RESPONSE.setStatus(200)
......
......@@ -35,4 +35,6 @@
Jensen, "HTTP Extensions for Distributed Authoring - WebDAV." RFC 2518.
Microsoft, U.C. Irvine, Netscape, Novell. February, 1999."""
enable_ms_public_header = False
from Zope2.Startup.config import ( # NOQA
ZSERVER_ENABLE_MS_PUBLIC_HEADER as enable_ms_public_header,
)
......@@ -5,9 +5,9 @@ from AccessControl.SecurityManagement import noSecurityManager
from AccessControl.SecurityManager import setSecurityPolicy
from Acquisition import Implicit
MS_DAV_AGENT = "Microsoft Data Access Internet Publishing Provider DAV"
def make_request_response(environ=None):
from StringIO import StringIO
from ZPublisher.HTTPRequest import HTTPRequest
......@@ -106,16 +106,16 @@ class TestResource(unittest.TestCase):
verifyClass(IWriteLock, self._getTargetClass())
def test_ms_public_header(self):
import webdav
from Zope2.Startup import config
default_settings = webdav.enable_ms_public_header
default_settings = config.ZSERVER_ENABLE_MS_PUBLIC_HEADER
try:
req, resp = make_request_response()
resource = self._makeOne()
resource.OPTIONS(req, resp)
self.assert_(not resp.headers.has_key('public'))
webdav.enable_ms_public_header = True
config.ZSERVER_ENABLE_MS_PUBLIC_HEADER = True
req, resp = make_request_response()
resource = self._makeOne()
resource.OPTIONS(req, resp)
......@@ -131,7 +131,7 @@ class TestResource(unittest.TestCase):
self.assert_(resp.headers['public'] == resp.headers['allow'])
finally:
webdav.enable_ms_public_header = default_settings
config.ZSERVER_ENABLE_MS_PUBLIC_HEADER = default_settings
def test_MOVE_self_locked(self):
"""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment