4
from miro import download_utils
6
from miro.test.framework import EventLoopTest, MiroTestCase
8
class TestingConnectionHandler(net.ConnectionHandler):
9
def __init__(self, test):
10
super(TestingConnectionHandler, self).__init__()
11
self.states['foo'] = self.handleFoo
12
self.states['bar'] = self.handleBar
13
self.states['noread'] = None
16
self.gotHandleClose = False
21
data = self.buffer.read()
23
self.test.stopEventLoop(False)
26
data = self.buffer.read()
28
self.test.stopEventLoop(False)
30
def handle_close(self, type):
31
self.gotHandleClose = True
33
self.test.stopEventLoop(False)
36
def __init__(self, closeCallback=None):
39
self.readCallback = None
40
self.closeCallback = closeCallback
42
self.connectionErrback = None
47
self.pendingOutput = ''
49
# to add a new page response, add the uri in the appropriate
50
# host, then add the appropriate response in _generateResponse
53
{'/normalpage.txt': 'I AM A NORMAL PAGE\n',
54
'/normalpage2.txt': 'I AM A NORMAL PAGE\n',
55
'/normalpage3.txt': 'I AM A NORMAL PAGE\n',
56
'/nohead.php': 'DYNAMIC CONTENT',
57
'/cookie.php': 'normal page',
58
'/etag.txt': 'normal page',
59
'/BasicAuthentication/': 'normal page',
60
'/DigestAuthentication/': 'normal page',
61
'/secure.txt': 'Normal',
64
{'/': "Normal", '/2': "Blah"},
66
{'/': "Normal", '/2': "Blah"},
68
{'/': "Normal", '/2': "Blah"},
70
{'/': "Normal", '/2': "Blah"},
72
{'/': "Normal", '/2': "Blah"},
75
def _tryReadCallback(self):
76
if ((len(self.pendingOutput) > 0 and self.readCallback
77
and not self.paused)):
78
response = self.pendingOutput
79
self.pendingOutput = ''
80
self.readCallback(response)
87
self._tryReadCallback()
89
def unpause_momentarily(self):
91
self._tryReadCallback()
94
def _generateResponse(self, method, uri, version, headers):
96
now = email.Utils.formatdate(usegmt=True)
97
if not self.pages.has_key(headers["Host"]):
98
self.errback(net.ConnectionError("Can't connect"))
101
host_pages = self.pages[headers["Host"]]
102
if host_pages.has_key(uri):
103
text = host_pages[uri]
107
if ((uri == '/BasicAuthentication/'
108
and (not headers.has_key('Authorization')
109
or headers['Authorization'] != 'Basic Z3Vlc3Q6Z3Vlc3Q='))):
110
text = "Not authorized"
112
"HTTP/1.1 401 Unauthorized",
113
"WWW-Authenticate: Basic realm=\"test\"",
114
"Content-Type: text/html; charset=UTF-8",
116
"Content-Length: %d" % len(text),
120
elif ((uri == '/DigestAuthentication/'
121
and (not headers.has_key('Authorization')
122
or (headers['Authorization'] != 'FOO')))):
123
text = "Not authorized"
125
"HTTP/1.1 401 Unauthorized",
126
"WWW-Authenticate: Digest realm=\"test\",domain=\"/DigestAuthentication\",nonce=\"13dc6f6b70fec989c0d5bd5956818b33\"",
127
"Content-Type: text/html; charset=UTF-8",
129
"Content-Length: %d" % len(text),
133
elif uri == '/etag.txt':
134
etag = "\"1262547188.66\""
135
if headers.get("If-None-Match") == etag:
137
"HTTP/1.1 304 Not Modified",
145
"Last-Modified: Sun, 03 Jan 2010 19:33:08 GMT",
146
"Content-Length: %d" % len(text),
147
"Content-Type: text/plain",
151
elif uri == '/cookie.php':
152
if "Cookie" in headers:
153
text += "\n%s" % headers["Cookie"]
156
"Content-Type: text/plain; charset=UTF-8",
157
"Set-Cookie: MiroTestCookie=foobar; path=/; domain=pculture.org",
158
"Last-Modified: %s" % now,
160
"Content-Length: %d" % len(text),
167
"Content-Type: text/plain; charset=UTF-8",
168
"Last-Modified: %s" % now,
170
"Content-Length: %d" % len(text),
174
elif method == "HEAD":
175
if uri == '/nohead.php':
177
"HTTP/1.1 405 NOT ALLOWED",
184
"Content-Type: text/plain; charset=UTF-8",
185
"Last-Modified: %s" % now,
187
"Content-Length: %d" % len(text),
191
text = "<h1>Not found</h1>"
193
"HTTP/1.1 404 Not Founde",
194
"Content-Type: text/html; charset=UTF-8",
196
"Content-Length: %d" % len(text),
200
def _processRequest(self, method, uri, version, headers):
201
response = self._generateResponse(method,uri, version, headers)
202
if response is not None:
203
self.pendingOutput += response
204
self._tryReadCallback()
206
def _processData(self, data):
207
self.unprocessed += data
208
while self.unprocessed.find("\r\n\r\n") != -1:
209
requests = self.unprocessed.split("\r\n\r\n", 1)
210
self.unprocessed = requests[1]
211
headers = requests[0].split("\r\n")
212
(req_method, req_uri, req_version) = headers.pop(0).split(' ')
213
headers = dict([x.split(': ', 1) for x in headers])
214
self._processRequest(req_method, req_uri, req_version, headers)
218
return "%s: %s" % (type(self).__name__, self.name)
220
return "Unknown %s" % type(self).__name__
222
def startReadTimeout(self):
225
def stopReadTimeout(self):
228
def open_connection(self, host, port, callback, errback,
229
disabledReadTimeout=None):
230
self.name = "Outgoing %s:%s" % (host, port)
235
self.errback = errback
236
self.dsiabledReadTimeout = disabledReadTimeout
239
def acceptConnection(self, host, port, callback, errback):
242
def close_connection(self):
248
def send_data(self, data, callback = None):
249
if not self.isOpen():
250
raise ValueError("Socket not connected")
252
self._processData(data)
254
def startReading(self, readCallback):
255
if not self.isOpen():
256
raise ValueError("Socket not connected")
257
self.readCallback = readCallback
258
self._tryReadCallback()
260
def stopReading(self):
261
"""Stop reading from the socket."""
262
if not self.isOpen():
263
raise ValueError("Socket not connected")
264
self.readCallback = None
266
def onReadTimeout(self):
267
raise IOError("Read Timeout")
269
def handleSocketError(self, code, msg, operation):
270
raise IOError("Socket Error")
272
class DumbFakeStream(FakeStream):
273
def _generateResponse(self, method, uri, version, headers):
276
class AsyncSocketTest(EventLoopTest):
279
self.errbackCalled = False
280
self.callbackCalled = False
281
self.fakeCallbackError = False
282
EventLoopTest.setUp(self)
284
def callback(self, data):
285
if self.fakeCallbackError:
288
self.callbackCalled = True
289
self.stopEventLoop(False)
291
def errback(self, error):
293
self.errbackCalled = True
294
self.stopEventLoop(False)
296
class NetworkBufferTest(MiroTestCase):
298
self.buffer = net.NetworkBuffer()
299
MiroTestCase.setUp(self)
301
def test_read_line(self):
302
self.buffer.addData("HEL")
303
self.assertEquals(self.buffer.readline(), None)
304
self.buffer.addData("LO\r\n")
305
self.assertEquals(self.buffer.readline(), 'HELLO')
306
self.buffer.addData("HOWS\r\nIT\nGOING\r\nCRONLY\rDOESNTBREAK")
307
self.assertEquals(self.buffer.readline(), 'HOWS')
308
self.assertEquals(self.buffer.readline(), 'IT')
309
self.assertEquals(self.buffer.readline(), 'GOING')
310
self.assertEquals(self.buffer.readline(), None)
311
self.assertEquals(self.buffer.read(), "CRONLY\rDOESNTBREAK")
314
self.buffer.addData("12345678901234567890")
315
self.assertEquals(self.buffer.read(4), "1234")
316
self.assertEquals(self.buffer.read(6), "567890")
317
self.buffer.addData("CARBOAT")
318
self.assertEquals(self.buffer.read(), "1234567890CARBOAT")
320
def test_length(self):
321
self.buffer.addData("ONE\r\nTWO")
322
self.assertEquals(self.buffer.length, 8)
323
self.buffer.readline()
324
self.assertEquals(self.buffer.length, 3)
326
self.assertEquals(self.buffer.length, 2)
327
self.buffer.unread("AAA")
328
self.assertEquals(self.buffer.length, 5)
329
self.buffer.addData("MORE")
330
self.assertEquals(self.buffer.length, 9)
332
def test_get_value(self):
333
self.buffer.addData("ONE")
334
self.buffer.addData("TWO")
335
self.buffer.addData("THREE")
336
self.assertEquals(self.buffer.getValue(), "ONETWOTHREE")
337
# check to make sure the value doesn't change as a result
338
self.assertEquals(self.buffer.getValue(), "ONETWOTHREE")
341
class WeirdCloseConnectionTest(AsyncSocketTest):
342
def test_close_during_open_connection(self):
344
Test opening a connection, then closing the HTTPConnection
345
before it happens. The open_connection callback shouldn't be
348
Open a socket on localhost and try to connect to that, this
349
should be pretty much instantaneous, so we don't need a long
350
timeout to runEventLoop.
352
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
353
sock.bind( ('127.0.0.1', 0))
355
host, port = sock.getsockname()
357
conn = net.AsyncSocket()
358
conn.open_connection(host, port, self.callback, self.errback)
359
conn.close_connection()
360
self.runEventLoop(timeout=1, timeoutNormal=True)
361
self.assert_(not self.callbackCalled)
362
self.assert_(self.errbackCalled)
366
def test_close_during_accept_connection(self):
368
Test opening a connection, then closing the HTTPConnection
369
before it happens. The open_connection callback shouldn't be
372
Open a socket on localhost and try to connect to that, this
373
should be pretty much instantaneous, so we don't need a long
374
timeout to runEventLoop.
376
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
378
conn = net.AsyncSocket()
379
conn.acceptConnection('127.0.0.1', 0, self.callback, self.errback)
380
sock.connect((conn.addr, conn.port))
381
conn.close_connection()
382
self.runEventLoop(timeout=1, timeoutNormal=True)
383
self.assert_(not self.callbackCalled)
384
self.assert_(self.errbackCalled)
388
class ConnectionHandlerTest(EventLoopTest):
390
EventLoopTest.setUp(self)
391
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
392
server.bind( ('127.0.0.1', 0) )
394
address = server.getsockname()
396
self.connectionHandler = TestingConnectionHandler(self)
397
def stopEventLoop(conn):
398
self.stopEventLoop(False)
399
self.connectionHandler.open_connection(address[0], address[1],
400
stopEventLoop, stopEventLoop)
402
self.remoteSocket, address = server.accept()
403
self.remoteSocket.setblocking(False)
406
data = 'abcabc' * 1024 * 64
407
self.connectionHandler.send_data(data)
408
self.received = net.NetworkBuffer()
411
readIn = self.remoteSocket.recv(1024 * 1024)
414
self.received.addData(readIn)
415
if self.received.length == len(data):
416
self.stopEventLoop(False)
418
self.add_timeout(0.1, readData, 'test')
419
self.add_timeout(0.1, readData, 'test')
421
self.assert_(self.received.read() == data)
424
self.connectionHandler.change_state('foo')
425
self.remoteSocket.send('abc')
426
self.runEventLoop(timeout=1)
427
self.assertEquals(self.connectionHandler.fooData, 'abc')
428
self.connectionHandler.change_state('bar')
429
self.remoteSocket.send('def')
430
self.runEventLoop(timeout=1)
431
self.assertEquals(self.connectionHandler.barData, 'def')
432
self.remoteSocket.send('ghi')
433
self.connectionHandler.change_state('noread')
434
self.runEventLoop(timeout=0.1, timeoutNormal=True)
435
self.assertEquals(self.connectionHandler.fooData, 'abc')
436
self.assertEquals(self.connectionHandler.barData, 'def')
437
self.connectionHandler.change_state('foo')
438
self.runEventLoop(timeout=1)
439
self.assertEquals(self.connectionHandler.fooData, 'abcghi')
441
def test_close(self):
442
self.connectionHandler.close_connection()
443
self.assert_(not self.connectionHandler.stream.isOpen())
444
# second close shouldn't throw any exceptions
445
self.connectionHandler.close_connection()
447
def test_remote_close(self):
448
self.connectionHandler.change_state('foo')
449
self.remoteSocket.shutdown(socket.SHUT_WR)
451
self.assertEquals(self.connectionHandler.gotHandleClose, True)
453
# FIXME - this test fails on Windows.
454
def test_remote_close2(self):
455
self.remoteSocket.shutdown(socket.SHUT_RD)
456
self.remoteSocket.close()
457
# Note: we have to send enough data so that the OS won't
458
# buffer the entire send call. Otherwise we may miss that the
460
self.connectionHandler.send_data("A" * 1024 * 1024)
461
self.runEventLoop(timeout=1)
462
self.assertEquals(self.connectionHandler.gotHandleClose, True)
464
def test_string(self):
465
# just make sure it doesn't throw an exception
466
str(self.connectionHandler)