1
##############################################################################
3
# Copyright (c) 2000-2004, Ng Pheng Siong. All Rights Reserved.
4
# This file is derived from Zope's ZServer/HTTPServer.py.
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
13
##############################################################################
16
Medusa HTTPS server for Zope
18
changes from Medusa's http_server:
20
Request Threads -- Requests are processed by threads from a thread
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.
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.
35
changes from Zope's HTTP server:
37
Well, this is a *HTTPS* server :)
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.
49
import sys, time, types
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
57
from M2Crypto import SSL, version
58
from medusa.https_server import https_server, https_channel
59
from medusa.asyncore import dispatcher
62
ZSERVER_SSL_VERSION=version
64
register_subsystem('ZServer HTTPS_Server')
67
class zhttps0_handler(zhttp_handler):
68
"zhttps0 handler - sets SSL request headers a la mod_ssl"
70
def __init__ (self, module, uri_base=None, env=None):
71
zhttp_handler.__init__(self, module, uri_base, env)
73
def get_environment(self, request):
74
env = zhttp_handler.get_environment(self, request)
75
env['SSL_CIPHER'] = request.channel.get_cipher()
79
class zhttps_handler(zhttps0_handler):
80
"zhttps handler - sets REMOTE_USER to user's X.509 certificate Subject DN"
82
def __init__ (self, module, uri_base=None, env=None):
83
zhttps0_handler.__init__(self, module, uri_base, env)
85
def get_environment(self, request):
86
env = zhttps0_handler.get_environment(self, request)
87
peer = request.channel.get_peer_cert()
89
env['REMOTE_USER'] = str(peer.get_subject())
93
class zhttps_channel(https_channel):
97
zombie_timeout=100*60 # 100 minutes
99
def __init__(self, server, conn, addr):
100
https_channel.__init__(self, server, conn, addr)
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
111
self.producer_fifo.push(producer)
112
if send: self.initiate_send()
114
push_with_producer=push
117
"try to handle a request"
121
try: module_name, request, response=self.queue.pop(0)
123
handle(module_name, request, response)
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()
138
#self.socket.set_shutdown(SSL.SSL_SENT_SHUTDOWN|SSL.SSL_RECEIVED_SHUTDOWN)
142
"Called when a publishing request is finished"
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:
154
class zhttps_server(https_server):
157
SERVER_IDENT='ZServerSSL/%s' % (ZSERVER_SSL_VERSION,)
159
channel_class = zhttps_channel
162
def __init__(self, ip, port, ssl_ctx, resolver=None, logger_object=None):
164
https_server.__init__(self, ip, port, ssl_ctx, resolver, logger_object)
165
self.ssl_ctx = ssl_ctx
167
self.log_info('(%s) HTTPS server started at %s\n'
168
'\tHostname: %s\n\tPort: %d' % (
170
time.ctime(time.time()),
175
def log_info(self, message, type='info'):
176
if self.shutup: return
177
dispatcher.log_info(self, message, type)
180
return self.accepting and \
181
len(asyncore.socket_map) < CONNECTION_LIMIT
183
def listen(self, num):
184
# override asyncore limits for nt's listen queue size
186
return self.socket.listen (num)