Commit fc052fac authored by Denis Bilenko's avatar Denis Bilenko

wsgi_test.py: bind to port 0 to avoid bind errors; make application into a test case method

parent b836ca87
...@@ -21,16 +21,17 @@ ...@@ -21,16 +21,17 @@
from gevent import monkey from gevent import monkey
monkey.patch_all(thread=False) monkey.patch_all(thread=False)
import sys
import cgi import cgi
import os import os
from greentest import TestCase, main
import urllib2 import urllib2
import greentest
import gevent import gevent
from gevent import wsgi from gevent import wsgi
from gevent import socket from gevent import socket
from greentest import find_command
try: try:
from cStringIO import StringIO from cStringIO import StringIO
...@@ -81,8 +82,9 @@ def chunked_post(env, start_response): ...@@ -81,8 +82,9 @@ def chunked_post(env, start_response):
class Site(object): class Site(object):
def __init__(self):
self.application = hello_world def __init__(self, application):
self.application = application
def __call__(self, env, start_response): def __call__(self, env, start_response):
return self.application(env, start_response) return self.application(env, start_response)
...@@ -126,21 +128,31 @@ def read_http(fd): ...@@ -126,21 +128,31 @@ def read_http(fd):
return response_line, headers, body return response_line, headers, body
class TestHttpd(TestCase): class TestCase(greentest.TestCase):
mode = 'static'
def listen(self):
return socket.tcp_listener(('0.0.0.0', 0))
def setUp(self): def setUp(self):
self.logfile = StringIO() self.logfile = sys.stderr # StringIO()
self.site = Site() self.site = Site(self.application)
listener = self.listen()
self.port = listener.getsockname()[1]
self.server = gevent.spawn( self.server = gevent.spawn(
wsgi.server, wsgi.server, listener, self.site, max_size=128, log=self.logfile)
socket.tcp_listener(('0.0.0.0', 12346)), self.site, max_size=128, log=self.logfile)
def tearDown(self): def tearDown(self):
self.server.kill(block=True) self.server.kill(block=True)
gevent.sleep(0) # XXX kill should be enough! # XXX server should have 'close' method which closes everything reliably
# XXX currently listening socket is kept open
class TestHttpdBasic(TestCase):
application = staticmethod(hello_world)
def test_001_server(self): def test_001_server(self):
sock = socket.connect_tcp(('127.0.0.1', 12346)) sock = socket.connect_tcp(('127.0.0.1', self.port))
sock.sendall('GET / HTTP/1.0\r\nHost: localhost\r\n\r\n') sock.sendall('GET / HTTP/1.0\r\nHost: localhost\r\n\r\n')
result = sock.makefile().read() result = sock.makefile().read()
sock.close() sock.close()
...@@ -149,7 +161,7 @@ class TestHttpd(TestCase): ...@@ -149,7 +161,7 @@ class TestHttpd(TestCase):
self.assert_(result.endswith('hello world')) self.assert_(result.endswith('hello world'))
def test_002_keepalive(self): def test_002_keepalive(self):
fd = socket.connect_tcp(('127.0.0.1', 12346)).makefile(bufsize=1) fd = socket.connect_tcp(('127.0.0.1', self.port)).makefile(bufsize=1)
fd.write('GET / HTTP/1.1\r\nHost: localhost\r\n\r\n') fd.write('GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
read_http(fd) read_http(fd)
fd.write('GET / HTTP/1.1\r\nHost: localhost\r\n\r\n') fd.write('GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
...@@ -158,7 +170,7 @@ class TestHttpd(TestCase): ...@@ -158,7 +170,7 @@ class TestHttpd(TestCase):
def test_003_passing_non_int_to_read(self): def test_003_passing_non_int_to_read(self):
# This should go in greenio_test # This should go in greenio_test
fd = socket.connect_tcp(('127.0.0.1', 12346)).makefile(bufsize=1) fd = socket.connect_tcp(('127.0.0.1', self.port)).makefile(bufsize=1)
fd.write('GET / HTTP/1.1\r\nHost: localhost\r\n\r\n') fd.write('GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
cancel = gevent.Timeout(1, RuntimeError) cancel = gevent.Timeout(1, RuntimeError)
self.assertRaises(TypeError, fd.read, "This shouldn't work") self.assertRaises(TypeError, fd.read, "This shouldn't work")
...@@ -166,7 +178,7 @@ class TestHttpd(TestCase): ...@@ -166,7 +178,7 @@ class TestHttpd(TestCase):
fd.close() fd.close()
def test_004_close_keepalive(self): def test_004_close_keepalive(self):
fd = socket.connect_tcp(('127.0.0.1', 12346)).makefile(bufsize=1) fd = socket.connect_tcp(('127.0.0.1', self.port)).makefile(bufsize=1)
fd.write('GET / HTTP/1.1\r\nHost: localhost\r\n\r\n') fd.write('GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
read_http(fd) read_http(fd)
fd.write('GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n') fd.write('GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
...@@ -176,15 +188,15 @@ class TestHttpd(TestCase): ...@@ -176,15 +188,15 @@ class TestHttpd(TestCase):
fd.close() fd.close()
def skip_test_005_run_apachebench(self): def skip_test_005_run_apachebench(self):
url = 'http://localhost:12346/' url = 'http://localhost:%s/' % self.port
# ab is apachebench # ab is apachebench
from gevent import processes from gevent import processes
out = processes.Process(find_command('ab'), out = processes.Process(greentest.find_command('ab'),
['-c','64','-n','1024', '-k', url]) ['-c','64','-n','1024', '-k', url])
print out.read() print out.read()
def test_006_reject_long_urls(self): def test_006_reject_long_urls(self):
fd = socket.connect_tcp(('127.0.0.1', 12346)).makefile(bufsize=1) fd = socket.connect_tcp(('127.0.0.1', self.port)).makefile(bufsize=1)
path_parts = [] path_parts = []
for ii in range(3000): for ii in range(3000):
path_parts.append('path') path_parts.append('path')
...@@ -196,15 +208,29 @@ class TestHttpd(TestCase): ...@@ -196,15 +208,29 @@ class TestHttpd(TestCase):
self.assertEqual(status, '414') self.assertEqual(status, '414')
fd.close() fd.close()
def test_007_get_arg(self): def test_008_correctresponse(self):
# define a new handler that does a get_arg as well as a read_body fd = socket.connect_tcp(('127.0.0.1', self.port)).makefile(bufsize=1)
def new_app(env, start_response): fd.write('GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
response_line_200,_,_ = read_http(fd)
fd.write('GET /notexist HTTP/1.1\r\nHost: localhost\r\n\r\n')
response_line_404,_,_ = read_http(fd)
fd.write('GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
response_line_test,_,_ = read_http(fd)
self.assertEqual(response_line_200,response_line_test)
fd.close()
class TestGetArg(TestCase):
def application(self, env, start_response):
body = env['wsgi.input'].read() body = env['wsgi.input'].read()
a = cgi.parse_qs(body).get('a', [1])[0] a = cgi.parse_qs(body).get('a', [1])[0]
start_response('200 OK', [('Content-type', 'text/plain')]) start_response('200 OK', [('Content-type', 'text/plain')])
return ['a is %s, body is %s' % (a, body)] return ['a is %s, body is %s' % (a, body)]
self.site.application = new_app
fd = socket.connect_tcp(('127.0.0.1', 12346)).makefile(bufsize=1) def test_007_get_arg(self):
# define a new handler that does a get_arg as well as a read_body
fd = socket.connect_tcp(('127.0.0.1', self.port)).makefile(bufsize=1)
request = '\r\n'.join(( request = '\r\n'.join((
'POST / HTTP/1.0', 'POST / HTTP/1.0',
'Host: localhost', 'Host: localhost',
...@@ -219,32 +245,28 @@ class TestHttpd(TestCase): ...@@ -219,32 +245,28 @@ class TestHttpd(TestCase):
self.assertEqual(body, 'a is a, body is a=a') self.assertEqual(body, 'a is a, body is a=a')
fd.close() fd.close()
def test_008_correctresponse(self):
fd = socket.connect_tcp(('127.0.0.1', 12346)).makefile(bufsize=1) class TestChunkedApp(TestCase):
fd.write('GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
response_line_200,_,_ = read_http(fd) application = staticmethod(chunked_app)
fd.write('GET /notexist HTTP/1.1\r\nHost: localhost\r\n\r\n')
response_line_404,_,_ = read_http(fd)
fd.write('GET / HTTP/1.1\r\nHost: localhost\r\n\r\n')
response_line_test,_,_ = read_http(fd)
self.assertEqual(response_line_200,response_line_test)
fd.close()
def test_009_chunked_response(self): def test_009_chunked_response(self):
fd = socket.connect_tcp(('127.0.0.1', 12346)).makefile(bufsize=1) fd = socket.connect_tcp(('127.0.0.1', self.port)).makefile(bufsize=1)
self.site.application = chunked_app
fd.write('GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n') fd.write('GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
self.assert_('Transfer-Encoding: chunked' in fd.read()) self.assert_('Transfer-Encoding: chunked' in fd.read())
def test_010_no_chunked_http_1_0(self): def test_010_no_chunked_http_1_0(self):
self.site.application = chunked_app fd = socket.connect_tcp(('127.0.0.1', self.port)).makefile(bufsize=1)
fd = socket.connect_tcp(('127.0.0.1', 12346)).makefile(bufsize=1)
fd.write('GET / HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n') fd.write('GET / HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n')
self.assert_('Transfer-Encoding: chunked' not in fd.read()) self.assert_('Transfer-Encoding: chunked' not in fd.read())
class TestBigChunks(TestCase):
application = staticmethod(big_chunks)
def test_011_multiple_chunks(self): def test_011_multiple_chunks(self):
self.site.application = big_chunks fd = socket.connect_tcp(('127.0.0.1', self.port)).makefile(bufsize=1)
fd = socket.connect_tcp(('127.0.0.1', 12346)).makefile(bufsize=1)
fd.write('GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n') fd.write('GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
_, headers = read_headers(fd) _, headers = read_headers(fd)
assert ('transfer-encoding', 'chunked') in headers.items(), headers assert ('transfer-encoding', 'chunked') in headers.items(), headers
...@@ -257,9 +279,13 @@ class TestHttpd(TestCase): ...@@ -257,9 +279,13 @@ class TestHttpd(TestCase):
chunklen = int(fd.readline(), 16) chunklen = int(fd.readline(), 16)
self.assert_(chunks > 1) self.assert_(chunks > 1)
class TestChunkedPost(TestCase):
application = staticmethod(chunked_post)
def test_014_chunked_post(self): def test_014_chunked_post(self):
self.site.application = chunked_post fd = socket.connect_tcp(('127.0.0.1', self.port)).makefile(bufsize=1)
fd = socket.connect_tcp(('127.0.0.1', 12346)).makefile(bufsize=1)
fd.write('PUT /a HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n' fd.write('PUT /a HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n'
'Transfer-Encoding: chunked\r\n\r\n' 'Transfer-Encoding: chunked\r\n\r\n'
'2\r\noh\r\n4\r\n hai\r\n0\r\n\r\n') '2\r\noh\r\n4\r\n hai\r\n0\r\n\r\n')
...@@ -267,7 +293,7 @@ class TestHttpd(TestCase): ...@@ -267,7 +293,7 @@ class TestHttpd(TestCase):
response = fd.read() response = fd.read()
self.assert_(response == 'oh hai', 'invalid response %s' % response) self.assert_(response == 'oh hai', 'invalid response %s' % response)
fd = socket.connect_tcp(('127.0.0.1', 12346)).makefile(bufsize=1) fd = socket.connect_tcp(('127.0.0.1', self.port)).makefile(bufsize=1)
fd.write('PUT /b HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n' fd.write('PUT /b HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n'
'Transfer-Encoding: chunked\r\n\r\n' 'Transfer-Encoding: chunked\r\n\r\n'
'2\r\noh\r\n4\r\n hai\r\n0\r\n\r\n') '2\r\noh\r\n4\r\n hai\r\n0\r\n\r\n')
...@@ -275,7 +301,7 @@ class TestHttpd(TestCase): ...@@ -275,7 +301,7 @@ class TestHttpd(TestCase):
response = fd.read() response = fd.read()
self.assert_(response == 'oh hai', 'invalid response %s' % response) self.assert_(response == 'oh hai', 'invalid response %s' % response)
fd = socket.connect_tcp(('127.0.0.1', 12346)).makefile(bufsize=1) fd = socket.connect_tcp(('127.0.0.1', self.port)).makefile(bufsize=1)
fd.write('PUT /c HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n' fd.write('PUT /c HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n'
'Transfer-Encoding: chunked\r\n\r\n' 'Transfer-Encoding: chunked\r\n\r\n'
'2\r\noh\r\n4\r\n hai\r\n0\r\n\r\n') '2\r\noh\r\n4\r\n hai\r\n0\r\n\r\n')
...@@ -284,34 +310,38 @@ class TestHttpd(TestCase): ...@@ -284,34 +310,38 @@ class TestHttpd(TestCase):
response = fd.read(8192) response = fd.read(8192)
self.assert_(response == 'oh hai', 'invalid response %s' % response) self.assert_(response == 'oh hai', 'invalid response %s' % response)
class TestUseWrite(TestCase):
application = staticmethod(use_write)
def test_015_write(self): def test_015_write(self):
self.site.application = use_write fd = socket.connect_tcp(('127.0.0.1', self.port)).makefile(bufsize=1)
fd = socket.connect_tcp(('127.0.0.1', 12346)).makefile(bufsize=1)
fd.write('GET /a HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n') fd.write('GET /a HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
response_line, headers, body = read_http(fd) response_line, headers, body = read_http(fd)
self.assert_('content-length' in headers) self.assert_('content-length' in headers)
fd = socket.connect_tcp(('127.0.0.1', 12346)).makefile(bufsize=1) fd = socket.connect_tcp(('127.0.0.1', self.port)).makefile(bufsize=1)
fd.write('GET /b HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n') fd.write('GET /b HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n')
response_line, headers, body = read_http(fd) response_line, headers, body = read_http(fd)
self.assert_('transfer-encoding' in headers) self.assert_('transfer-encoding' in headers)
self.assert_(headers['transfer-encoding'] == 'chunked') self.assert_(headers['transfer-encoding'] == 'chunked')
class TestHttps(TestCase): class TestHttps(greentest.TestCase):
mode = 'static'
def test_012_ssl_server(self): def application(self, environ, start_response):
def wsgi_app(environ, start_response):
start_response('200 OK', {}) start_response('200 OK', {})
return [environ['wsgi.input'].read()] return [environ['wsgi.input'].read()]
def test_012_ssl_server(self):
certificate_file = os.path.join(os.path.dirname(__file__), 'test_server.crt') certificate_file = os.path.join(os.path.dirname(__file__), 'test_server.crt')
private_key_file = os.path.join(os.path.dirname(__file__), 'test_server.key') private_key_file = os.path.join(os.path.dirname(__file__), 'test_server.key')
sock = socket.ssl_listener(('', 4201), private_key_file, certificate_file) sock = socket.ssl_listener(('', 4201), private_key_file, certificate_file)
g = gevent.spawn(wsgi.server, sock, wsgi_app) g = gevent.spawn(wsgi.server, sock, self.application)
try: try:
req = HTTPRequest("https://localhost:4201/foo", method="POST", data='abc') req = HTTPRequest("https://localhost:4201/foo", method="POST", data='abc')
f = urllib2.urlopen(req) f = urllib2.urlopen(req)
...@@ -321,14 +351,10 @@ class TestHttps(TestCase): ...@@ -321,14 +351,10 @@ class TestHttps(TestCase):
g.kill() # XXX use blocking kill g.kill() # XXX use blocking kill
def test_013_empty_return(self): def test_013_empty_return(self):
def wsgi_app(environ, start_response):
start_response("200 OK", [])
return [""]
certificate_file = os.path.join(os.path.dirname(__file__), 'test_server.crt') certificate_file = os.path.join(os.path.dirname(__file__), 'test_server.crt')
private_key_file = os.path.join(os.path.dirname(__file__), 'test_server.key') private_key_file = os.path.join(os.path.dirname(__file__), 'test_server.key')
sock = socket.ssl_listener(('', 4202), private_key_file, certificate_file) sock = socket.ssl_listener(('', 4202), private_key_file, certificate_file)
g = gevent.spawn(wsgi.server, sock, wsgi_app) g = gevent.spawn(wsgi.server, sock, self.application)
try: try:
req = HTTPRequest("https://localhost:4202/foo") req = HTTPRequest("https://localhost:4202/foo")
f = urllib2.urlopen(req) f = urllib2.urlopen(req)
...@@ -351,4 +377,4 @@ class HTTPRequest(urllib2.Request): ...@@ -351,4 +377,4 @@ class HTTPRequest(urllib2.Request):
return self.method return self.method
if __name__ == '__main__': if __name__ == '__main__':
main() greentest.main()
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