[Zope-Checkins] CVS: Zope/lib/python/ZServer - ClockServer.py:1.1.2.1 component.xml:1.3.38.1 datatypes.py:1.3.16.1

Chris McDonough chrism at plope.com
Thu Dec 25 04:08:16 EST 2003


Update of /cvs-repository/Zope/lib/python/ZServer
In directory cvs.zope.org:/tmp/cvs-serv22553/lib/python/ZServer

Modified Files:
      Tag: chrism-scheduling-branch
	component.xml datatypes.py 
Added Files:
      Tag: chrism-scheduling-branch
	ClockServer.py 
Log Message:
Initial clock server implementation.  The clock server generates a faux http request to a traversable method at a regular interval using arbitrary authentication credentials without the aid of an external process (it uses ZServer internals to schedule the request like any other Zope request).  The method, the interval, and the credentials are configurable via the Zope config file.  More than one clock server can be caused to run per instance.


=== Added File Zope/lib/python/ZServer/ClockServer.py ===
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (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
#
##############################################################################

""" Zope clock server.  Generate a faux HTTP request on a regular basis
by coopting the asyncore API. """

import sys, string, os, socket, time, StringIO, base64
import asyncore
from medusa.thread.select_trigger import trigger
from medusa.http_server import http_request
from medusa.default_handler import unquote
from Lifetime.timeslice import timeslice
from PubCore import handle
from HTTPResponse import make_response
from ZPublisher.HTTPRequest import HTTPRequest

_last_slices = {} # one entry will be in this map per clock server

class LogHelper:
    def __init__(self, logger):
        self.logger = logger

    def log(self, ip, msg, **kw):
        self.logger.log(ip + ' ' + msg)

class DummyChannel:
    # we need this minimal do-almost-nothing channel class to appease medusa
    addr = ['127.0.0.1']
    closed = 1

    def __init__(self, server):
        self.server = server
        
    def push_with_producer(self):
        pass

    def close_when_done(self):
        pass
    
class ClockServer(asyncore.dispatcher):
    SERVER_IDENT = 'Zope Clock' # required by ZServer

    def __init__ (self, method, period=60, user=None, password=None,
                  host=None, logger=None):
        self.myid = id(self)
        _last_slices[self.myid] = timeslice(period)
        self.period = period
        self.method = method

        h = self.headers = []
        h.append('User-Agent: Zope Clock Server Client')
        h.append('Accept: text/html,text/plain')
        if not host:
            host = socket.gethostname()
        h.append('Host: %s' % host)
        auth = False
        if user and password:
            encoded = base64.encodestring('%s:%s' % (user, password))
            encoded = encoded.replace('\012', '')
            h.append('Authorization: Basic %s' % encoded)
            auth = True

        asyncore.dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.logger = LogHelper(logger)
        self.log_info('Clock server for "%s" started (user: %s, period: %s)'
                      % (method, auth and user or 'Anonymous', self.period))

    def get_requests_and_response(self):
        out = StringIO.StringIO()
        s_req = '%s %s HTTP/%s' % ('GET', self.method, '1.0')
        req = http_request(DummyChannel(self), s_req, 'GET', self.method,
                           '1.0', self.headers)
        env = self.get_env(req)
        resp = make_response(req, env)
        zreq = HTTPRequest(out, env, resp)
        return req, zreq, resp

    def get_env(self, req):
        env = {}
        (path, params, query, fragment) = req.split_uri()
        if params:
            path = path + params # undo medusa bug
        while path and path[0] == '/':
            path = path[1:]
        if '%' in path:
            path = unquote(path)
        if query:
            # ZPublisher doesn't want the leading '?'
            query = query[1:]
        env['REQUEST_METHOD']='GET'
        env['SERVER_PORT']='Clock'
        env['SERVER_NAME']='Zope Clock Server'
        env['SERVER_SOFTWARE']='Zope'
        env['SERVER_PROTOCOL']='HTTP/1.0'
        env['channel.creation_time']=time.time()
        env['SCRIPT_NAME']=''
        env['PATH_INFO']= '/' + path
        env['PATH_TRANSLATED']= os.path.normpath(
            os.path.join(os.getcwd(), env['PATH_INFO']))
        if query:
            env['QUERY_STRING'] = query
        env['GATEWAY_INTERFACE']='CGI/1.1'
        env['REMOTE_ADDR']='0'
        for header in req.header:
            key,value=header.split(":",1)
            key=key.upper()
            value=value.strip()
            key='HTTP_%s' % ("_".join(key.split( "-")))
            if value:
                env[key]=value
        return env

    def readable(self):
        # generate a request at most once every self.period seconds
        slice = timeslice(self.period)
        if slice != _last_slices.get(self.myid):
            # no need for threadsafety here, as we're only ever in one thread
            _last_slices[self.myid] = slice
            req, zreq, resp = self.get_requests_and_response()
            handle('Zope', zreq, resp)
        return 0

    def handle_read(self):
        pass

    def handle_write (self):
        self.log_info ('unexpected write event', 'warning')

    def writable(self):
        return 0

    def handle_error (self):      # don't close the socket on error
        (file,fun,line), t, v, tbinfo = asyncore.compact_traceback()
        self.log_info('Problem in Clock (%s:%s %s)' % (t, v, tbinfo),
                      'error')





=== Zope/lib/python/ZServer/component.xml 1.3 => 1.3.38.1 ===
--- Zope/lib/python/ZServer/component.xml:1.3	Mon Mar 24 17:32:39 2003
+++ Zope/lib/python/ZServer/component.xml	Thu Dec 25 04:07:45 2003
@@ -58,4 +58,14 @@
      <key name="address" datatype="inet-address"/>
   </sectiontype>
 
+  <sectiontype name="clock-server"
+               datatype=".ClockServerFactory"
+               implements="ZServer.server">
+     <key name="method" datatype="string"/>
+     <key name="period" datatype="integer" default="60"/>
+     <key name="user" datatype="string" />
+     <key name="password" datatype="string"/>
+     <key name="host" datatype="string"/>
+  </sectiontype>
+
 </component>


=== Zope/lib/python/ZServer/datatypes.py 1.3 => 1.3.16.1 ===
--- Zope/lib/python/ZServer/datatypes.py:1.3	Wed Oct  1 13:23:03 2003
+++ Zope/lib/python/ZServer/datatypes.py	Thu Dec 25 04:07:45 2003
@@ -168,3 +168,19 @@
     def create(self):
         from ZServer.ICPServer import ICPServer
         return ICPServer(self.host, self.port)
+
+class ClockServerFactory(ServerFactory):
+    def __init__(self, section):
+        ServerFactory.__init__(self)
+        self.method = section.method
+        self.period = section.period
+        self.user = section.user
+        self.password = section.password
+        self.host = section.host
+
+    def create(self):
+        from ZServer.ClockServer import ClockServer
+        from ZServer.AccessLogger import access_logger
+        return ClockServer(self.method, self.period, self.user,
+                           self.password, self.host, access_logger)
+    




More information about the Zope-Checkins mailing list