Klaus Demo bjoern / 261d3a6
Added some tests Jonas Haag 9 years ago
7 changed file(s) with 248 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 import random
1
2 def invalid_header_type(environ, start_response):
3 start_response('200 ok', None)
4 return ['yo']
5
6 def invalid_header_tuple(environ, start_response):
7 tuples = {1: (), 2: ('a', 'b', 'c'), 3: ('a',)}
8 start_response('200 ok', [tuples[random.randint(1, 3)]])
9 return ['yo']
10
11 def invalid_header_tuple_item(environ, start_response):
12 start_response('200 ok', (object(), object()))
13 return ['yo']
14
15 import bjoern
16 bjoern.run(invalid_header_type, '0.0.0.0', 8080)
0 def app(e, s):
1 s('200 ok', [])
2 return ''
3
4 import bjoern
5 bjoern.run(app, '0.0.0.0', 8080)
0 import os
1
2 FILES = {
3 'small' : 888,
4 'big' : 88888
5 }
6
7 for name, size in FILES.items():
8 new_name = '/tmp/bjoern.%s.tmp' % name
9 with open(new_name, 'w') as f:
10 f.write(os.urandom(size))
11 FILES[name] = new_name
12
13 def app(env, start_response):
14 start_response('200 ok', [])
15 if env['PATH_INFO'].startswith('/big'):
16 return open(FILES['big'])
17 return open(FILES['small'])
18
19 import bjoern
20 bjoern.run(app, '0.0.0.0', 8080)
0 import pprint
1 import bjoern
2
3 def app(env, start_response):
4 pprint.pprint(env)
5 start_response('200 yo', [])
6 return []
7
8 bjoern.run(app, '0.0.0.0', 8080)
0 N = 1024
1 CHUNK = 'a' * 1024
2 DATA_LEN = N * len(CHUNK)
3
4 class _iter(object):
5 def __iter__(self):
6 for i in xrange(N):
7 yield CHUNK
8
9 def app(e, s):
10 s('200 ok', [('Content-Length', str(DATA_LEN))])
11 return _iter()
12
13 import bjoern
14 bjoern.run(app, '0.0.0.0', 8080)
0 import os
1 import re
2 import random
3 import httplib
4 import socket
5
6 HOST = ('127.0.0.1', 9000)
7
8 class Fail(Exception):
9 pass
10
11 def dispatcher(environ, start_response):
12 return dispatcher.app(environ, start_response)
13
14 def still_alive(sock):
15 prev_timeout = sock.gettimeout()
16 try:
17 sock.settimeout(0.5)
18 try:
19 c = sock.recv(100)
20 assert not c, repr(c)
21 return False
22 except socket.timeout:
23 return True
24 finally:
25 sock.settimeout(prev_timeout)
26
27
28 class NeverClosedHTTPConnection(httplib.HTTPConnection):
29 def close(self):
30 backup = self.sock
31 self.sock = None
32 httplib.HTTPConnection.close(self)
33 self.sock = backup
34
35 class Testcase(object):
36 body_length = 1000
37
38 def __init__(self):
39 self.conn = None
40 self.body = os.urandom(self.body_length)
41 self.request_count = 5
42
43 def run(self):
44 if not self.conn:
45 dispatcher.app = self
46 self.conn = NeverClosedHTTPConnection(*HOST)
47
48 self.send_request(self._tinker_request())
49 response = self.get_response()
50 if self.expect_chunked and not response.chunked:
51 raise Fail("Response unexpectedly not chunked")
52 body = response.read()
53
54 if self.raise_error:
55 if response.status != 500 or body:
56 raise Fail("Expected 500 Internal Server Error")
57 else:
58 if response.status != 200:
59 raise Fail("Status is %d, expected 200", response.status)
60
61 if still_alive(self.conn.sock):
62 if not self.expect_keep_alive:
63 raise Fail("Expected connection not be kept-alive")
64 self.request_count -= 1
65 if self.request_count:
66 if self.request_count == 1:
67 # Send Connection: close on last request
68 self.want_keep_alive = False
69 self.expect_keep_alive = False
70 self.expect_chunked = False
71 self.run()
72 else:
73 if still_alive(self.conn.sock):
74 raise Fail("Connection still alive")
75 else:
76 if self.expect_keep_alive:
77 raise Fail("Expected connection to be kept-alive")
78
79 if not self.raise_error and body != self.body:
80 raise Fail("Different bodies:\n%s\n%s" % (body, self.body))
81
82 def _is_chunked(self, response):
83 if not 'Transfer-Encoding: chunked' in response or not response.endswith('0\r\n\r\n'):
84 return False
85 # TODO: full-featured chunked validation here
86 return True
87
88 def _tinker_request(self):
89 req = 'GET / HTTP/1.%d\r\n' % self.http_minor
90 if self.want_keep_alive:
91 req += 'Connection: Keep-Alive\r\n'
92 req += '\r\n'
93 return req
94
95 def send_request(self, data):
96 self.conn.send(data)
97
98 def get_response(self):
99 self.conn._HTTPConnection__state = httplib._CS_REQ_SENT
100 return self.conn.getresponse()
101
102 def __call__(self, environ, start_response):
103 if self.raise_error and random.randint(0, 1):
104 raise ValueError('foo')
105 headers = []
106 if self.give_content_length:
107 headers.append(('Content-Length', str(len(self.body))))
108 if self.raise_error:
109 start_response('200 ok', headers)
110 raise ValueError('bar')
111 start_response('200 ok', headers)
112 # second item is to trick bjoern's internal optimizations:
113 return [self.body, '']
114
115 import thread
116 import bjoern
117 thread.start_new_thread(bjoern.run, (dispatcher,)+HOST)
118
119 import time; time.sleep(0.1)
120
121 for index, tpl in enumerate([
122 (0, False, False, False, False, False),
123 (0, False, False, True, False, False),
124 (0, False, False, True, False, False),
125 (0, False, True, True, False, True),
126 (0, True, False, False, False, False),
127 (0, True, False, True, False, False),
128 (0, True, True, False, False, False),
129 (0, True, True, True, False, False),
130
131 (1, False, False, False, False, False),
132 (1, False, False, True, True, True),
133 (1, False, True, False, False, False),
134 (1, False, True, True, False, True),
135 (1, True, False, False, False, False),
136 (1, True, False, True, False, False),
137 (1, True, True, False, False, False),
138 (1, True, True, True, False, False)
139 ]):
140 print 'Running test %d: %r' % (index, tpl)
141 class _test(Testcase):
142 http_minor, \
143 raise_error, \
144 give_content_length, \
145 want_keep_alive, \
146 expect_chunked, \
147 expect_keep_alive = tpl
148 _test().run()
149
150 print '--- SUCCESS! ---'
0 import sys
1 import socket
2
3 conn = socket.create_connection(('0.0.0.0', 8080))
4 msgs = [
5 # Keep-Alive, Transfer-Encoding chunked
6 'GET / HTTP/1.1\r\nConnection: Keep-Alive\r\n\r\n',
7 # Close, EOF "encoding"
8 'GET / HTTP/1.1\r\n\r\n',
9 'GET / HTTP/1.1\r\nConnection: close\r\n\r\n',
10 'GET / HTTP/1.0\r\nConnection: Keep-Alive\r\n\r\n',
11 # Bad Request
12 'GET /%20%20% HTTP/1.1\r\n\r\n',
13 # Bug #14
14 'GET /%20abc HTTP/1.0\r\n\r\n',
15 # Content-{Length, Type}
16 'GET / HTTP/1.0\r\nContent-Length: 11\r\n'
17 'Content-Type: text/blah\r\nContent-Fype: bla\r\n'
18 'Content-Tength: bla\r\n\r\nhello world'
19 ]
20 conn.send(msgs[int(sys.argv[1])])
21 while 1:
22 data = conn.recv(100)
23 if not data: break
24 print repr(data)
25 if data.endswith('0\r\n\r\n'):
26 if raw_input('new request? Y/n') == 'n':
27 exit()
28 conn.send('GET / HTTP/1.1\r\nConnection: Keep-Alive\r\n\r\n')