~ubuntu-branches/ubuntu/maverick/m2crypto/maverick

« back to all changes in this revision

Viewing changes to demo/Zope/ZServer/HTTPS_Server.py

  • Committer: Bazaar Package Importer
  • Author(s): Dima Barsky
  • Date: 2004-03-30 21:54:28 UTC
  • Revision ID: james.westby@ubuntu.com-20040330215428-7zulfxz4xt31w2xr
Tags: upstream-0.13
ImportĀ upstreamĀ versionĀ 0.13

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
##############################################################################
 
2
#
 
3
# Copyright (c) 2000-2004, Ng Pheng Siong. All Rights Reserved.
 
4
# This file is derived from Zope's ZServer/HTTPServer.py.
 
5
#
 
6
# This software is subject to the provisions of the Zope Public License,
 
7
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
 
8
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
 
9
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 
10
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 
11
# FOR A PARTICULAR PURPOSE
 
12
#
 
13
##############################################################################
 
14
 
 
15
"""
 
16
Medusa HTTPS server for Zope
 
17
 
 
18
changes from Medusa's http_server:
 
19
 
 
20
    Request Threads -- Requests are processed by threads from a thread
 
21
    pool.
 
22
    
 
23
    Output Handling -- Output is pushed directly into the producer
 
24
    fifo by the request-handling thread. The HTTP server does not do
 
25
    any post-processing such as chunking.
 
26
 
 
27
    Pipelineable -- This is needed for protocols such as HTTP/1.1 in
 
28
    which mutiple requests come in on the same channel, before
 
29
    responses are sent back. When requests are pipelined, the client
 
30
    doesn't wait for the response before sending another request. The
 
31
    server must ensure that responses are sent back in the same order
 
32
    as requests are received.
 
33
 
 
34
 
 
35
changes from Zope's HTTP server:
 
36
 
 
37
    Well, this is a *HTTPS* server :)
 
38
 
 
39
    X.509 certificate-based authentication -- When this is in force,
 
40
    zhttps_handler, a subclass of zhttp_handler, is installed.  The
 
41
    https server is configured to request an X.509 certificate from
 
42
    the client. When the request reaches zhttps_handler, it sets
 
43
    REMOTE_USER to the client's subject distinguished name (DN) from
 
44
    the certificate. Zope's REMOTE_USER machinery takes care of the
 
45
    rest, e.g., in conjunction with the RemoteUserFolder product.
 
46
    
 
47
""" 
 
48
 
 
49
import sys, time, types
 
50
 
 
51
from PubCore import handle
 
52
from medusa import asyncore
 
53
from ZServer import CONNECTION_LIMIT, ZOPE_VERSION
 
54
from HTTPServer import zhttp_handler
 
55
from zLOG import register_subsystem
 
56
 
 
57
from M2Crypto import SSL, version
 
58
from medusa.https_server import https_server, https_channel
 
59
from medusa.asyncore import dispatcher
 
60
 
 
61
 
 
62
ZSERVER_SSL_VERSION=version
 
63
 
 
64
register_subsystem('ZServer HTTPS_Server')
 
65
 
 
66
 
 
67
class zhttps0_handler(zhttp_handler):
 
68
    "zhttps0 handler - sets SSL request headers a la mod_ssl"
 
69
 
 
70
    def __init__ (self, module, uri_base=None, env=None):
 
71
        zhttp_handler.__init__(self, module, uri_base, env)
 
72
 
 
73
    def get_environment(self, request):
 
74
        env = zhttp_handler.get_environment(self, request)
 
75
        env['SSL_CIPHER'] = request.channel.get_cipher()
 
76
        return env
 
77
 
 
78
 
 
79
class zhttps_handler(zhttps0_handler):
 
80
    "zhttps handler - sets REMOTE_USER to user's X.509 certificate Subject DN"
 
81
 
 
82
    def __init__ (self, module, uri_base=None, env=None):
 
83
        zhttps0_handler.__init__(self, module, uri_base, env)
 
84
 
 
85
    def get_environment(self, request):
 
86
        env = zhttps0_handler.get_environment(self, request)
 
87
        peer = request.channel.get_peer_cert()
 
88
        if peer is not None:
 
89
            env['REMOTE_USER'] = str(peer.get_subject())
 
90
        return env
 
91
 
 
92
 
 
93
class zhttps_channel(https_channel):
 
94
    "https channel"
 
95
 
 
96
    closed=0
 
97
    zombie_timeout=100*60 # 100 minutes
 
98
    
 
99
    def __init__(self, server, conn, addr):
 
100
        https_channel.__init__(self, server, conn, addr)
 
101
        self.queue=[]
 
102
        self.working=0
 
103
        self.peer_found=0
 
104
    
 
105
    def push(self, producer, send=1):
 
106
        # this is thread-safe when send is false
 
107
        # note, that strings are not wrapped in 
 
108
        # producers by default
 
109
        if self.closed:
 
110
            return
 
111
        self.producer_fifo.push(producer)
 
112
        if send: self.initiate_send()
 
113
        
 
114
    push_with_producer=push
 
115
 
 
116
    def work(self):
 
117
        "try to handle a request"
 
118
        if not self.working:
 
119
            if self.queue:
 
120
                self.working=1
 
121
                try: module_name, request, response=self.queue.pop(0)
 
122
                except: return
 
123
                handle(module_name, request, response)
 
124
        
 
125
    def close(self):
 
126
        self.closed=1
 
127
        while self.queue:
 
128
            self.queue.pop()
 
129
        if self.current_request is not None:
 
130
            self.current_request.channel=None # break circ refs
 
131
            self.current_request=None
 
132
        while self.producer_fifo:
 
133
            p=self.producer_fifo.first()
 
134
            if p is not None and type(p) != types.StringType:
 
135
                p.more() # free up resources held by producer
 
136
            self.producer_fifo.pop()
 
137
        self.del_channel()
 
138
        #self.socket.set_shutdown(SSL.SSL_SENT_SHUTDOWN|SSL.SSL_RECEIVED_SHUTDOWN)
 
139
        self.socket.close()
 
140
 
 
141
    def done(self):
 
142
        "Called when a publishing request is finished"
 
143
        self.working=0
 
144
        self.work()
 
145
 
 
146
    def kill_zombies(self):
 
147
        now = int (time.time())
 
148
        for channel in asyncore.socket_map.values():
 
149
            if channel.__class__ == self.__class__:
 
150
                if (now - channel.creation_time) > channel.zombie_timeout:
 
151
                    channel.close()
 
152
 
 
153
 
 
154
class zhttps_server(https_server):    
 
155
    "https server"
 
156
    
 
157
    SERVER_IDENT='ZServerSSL/%s' % (ZSERVER_SSL_VERSION,)
 
158
    
 
159
    channel_class = zhttps_channel
 
160
    shutup = 0
 
161
 
 
162
    def __init__(self, ip, port, ssl_ctx, resolver=None, logger_object=None):
 
163
        self.shutup = 1
 
164
        https_server.__init__(self, ip, port, ssl_ctx, resolver, logger_object)
 
165
        self.ssl_ctx = ssl_ctx
 
166
        self.shutup = 0        
 
167
        self.log_info('(%s) HTTPS server started at %s\n'
 
168
                      '\tHostname: %s\n\tPort: %d' % (
 
169
                        self.SERVER_IDENT,
 
170
                        time.ctime(time.time()),
 
171
                        self.server_name,
 
172
                        self.server_port
 
173
                        ))
 
174
        
 
175
    def log_info(self, message, type='info'):
 
176
        if self.shutup: return
 
177
        dispatcher.log_info(self, message, type)
 
178
 
 
179
    def readable(self):
 
180
        return self.accepting and \
 
181
                len(asyncore.socket_map) < CONNECTION_LIMIT
 
182
 
 
183
    def listen(self, num):
 
184
        # override asyncore limits for nt's listen queue size
 
185
        self.accepting = 1
 
186
        return self.socket.listen (num)
 
187