You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
148 lines
4.0 KiB
148 lines
4.0 KiB
##############################################################################
|
|
#
|
|
# Copyright (c) 2005 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.
|
|
#
|
|
##############################################################################
|
|
"""Tests for waitress.channel maintenance logic
|
|
"""
|
|
import doctest
|
|
|
|
|
|
class FakeSocket: # pragma: no cover
|
|
data = ""
|
|
setblocking = lambda *_: None
|
|
close = lambda *_: None
|
|
|
|
def __init__(self, no):
|
|
self.no = no
|
|
|
|
def fileno(self):
|
|
return self.no
|
|
|
|
def getpeername(self):
|
|
return ("localhost", self.no)
|
|
|
|
def send(self, data):
|
|
self.data += data
|
|
return len(data)
|
|
|
|
def recv(self, data):
|
|
return "data"
|
|
|
|
|
|
def zombies_test():
|
|
"""Regression test for HTTPChannel.maintenance method
|
|
|
|
Bug: This method checks for channels that have been "inactive" for a
|
|
configured time. The bug was that last_activity is set at creation time
|
|
but never updated during async channel activity (reads and writes), so
|
|
any channel older than the configured timeout will be closed when a new
|
|
channel is created, regardless of activity.
|
|
|
|
>>> import time
|
|
>>> import waitress.adjustments
|
|
>>> config = waitress.adjustments.Adjustments()
|
|
|
|
>>> from waitress.server import HTTPServer
|
|
>>> class TestServer(HTTPServer):
|
|
... def bind(self, (ip, port)):
|
|
... print "Listening on %s:%d" % (ip or '*', port)
|
|
>>> sb = TestServer('127.0.0.1', 80, start=False, verbose=True)
|
|
Listening on 127.0.0.1:80
|
|
|
|
First we confirm the correct behavior, where a channel with no activity
|
|
for the timeout duration gets closed.
|
|
|
|
>>> from waitress.channel import HTTPChannel
|
|
>>> socket = FakeSocket(42)
|
|
>>> channel = HTTPChannel(sb, socket, ('localhost', 42))
|
|
|
|
>>> channel.connected
|
|
True
|
|
|
|
>>> channel.last_activity -= int(config.channel_timeout) + 1
|
|
|
|
>>> channel.next_channel_cleanup[0] = channel.creation_time - int(
|
|
... config.cleanup_interval) - 1
|
|
|
|
>>> socket2 = FakeSocket(7)
|
|
>>> channel2 = HTTPChannel(sb, socket2, ('localhost', 7))
|
|
|
|
>>> channel.connected
|
|
False
|
|
|
|
Write Activity
|
|
--------------
|
|
|
|
Now we make sure that if there is activity the channel doesn't get closed
|
|
incorrectly.
|
|
|
|
>>> channel2.connected
|
|
True
|
|
|
|
>>> channel2.last_activity -= int(config.channel_timeout) + 1
|
|
|
|
>>> channel2.handle_write()
|
|
|
|
>>> channel2.next_channel_cleanup[0] = channel2.creation_time - int(
|
|
... config.cleanup_interval) - 1
|
|
|
|
>>> socket3 = FakeSocket(3)
|
|
>>> channel3 = HTTPChannel(sb, socket3, ('localhost', 3))
|
|
|
|
>>> channel2.connected
|
|
True
|
|
|
|
Read Activity
|
|
--------------
|
|
|
|
We should test to see that read activity will update a channel as well.
|
|
|
|
>>> channel3.connected
|
|
True
|
|
|
|
>>> channel3.last_activity -= int(config.channel_timeout) + 1
|
|
|
|
>>> import waitress.parser
|
|
>>> channel3.parser_class = (
|
|
... waitress.parser.HTTPRequestParser)
|
|
>>> channel3.handle_read()
|
|
|
|
>>> channel3.next_channel_cleanup[0] = channel3.creation_time - int(
|
|
... config.cleanup_interval) - 1
|
|
|
|
>>> socket4 = FakeSocket(4)
|
|
>>> channel4 = HTTPChannel(sb, socket4, ('localhost', 4))
|
|
|
|
>>> channel3.connected
|
|
True
|
|
|
|
Main loop window
|
|
----------------
|
|
|
|
There is also a corner case we'll do a shallow test for where a
|
|
channel can be closed waiting for the main loop.
|
|
|
|
>>> channel4.last_activity -= 1
|
|
|
|
>>> last_active = channel4.last_activity
|
|
|
|
>>> channel4.set_async()
|
|
|
|
>>> channel4.last_activity != last_active
|
|
True
|
|
|
|
"""
|
|
|
|
|
|
def test_suite():
|
|
return doctest.DocTestSuite()
|