1# -*- coding: utf-8 -*- 2""" 3Unittests for http serving module 4""" 5 6import sys 7 8import unittest 9 10from urllib.parse import urlsplit, quote, quote_plus, unquote, unquote_plus 11 12 13import os 14import time 15import tempfile 16import shutil 17import socket 18import errno 19 20try: 21 import simplejson as json 22except ImportError: 23 import json 24 25# Import ioflo libs 26from ioflo.aid.sixing import * 27from ioflo.aid.odicting import odict 28from ioflo.aid.timing import Timer, StoreTimer 29from ioflo.aid.consoling import getConsole 30from ioflo.base import storing 31 32from ioflo.aio import wiring 33from ioflo.aio.http import httping, clienting, serving 34 35console = getConsole() 36 37 38def setUpModule(): 39 pass 40 41def tearDownModule(): 42 pass 43 44 45class BasicTestCase(unittest.TestCase): 46 """ 47 Test Case 48 """ 49 50 def setUp(self): 51 """ 52 53 """ 54 console.reinit(verbosity=console.Wordage.profuse) 55 56 tempdirpath = os.path.dirname( 57 os.path.dirname( 58 os.path.dirname( 59 os.path.abspath( 60 sys.modules.get(__name__).__file__)))) 61 self.certdirpath = os.path.join(tempdirpath, 'test', 'tls', 'certs') 62 63 def tearDown(self): 64 """ 65 66 """ 67 console.reinit(verbosity=console.Wordage.concise) 68 69 def testHttpError(self): 70 """ 71 Test HTTPError class 72 """ 73 console.terse("{0}\n".format(self.testHttpError.__doc__)) 74 75 error = httping.HTTPError(status=400) 76 self.assertEqual(error.status, 400) 77 self.assertEqual(error.reason, 'Bad Request') 78 self.assertEqual(error.title, "") 79 self.assertEqual(error.detail, "") 80 self.assertIs(error.fault, None) 81 self.assertEqual(error.headers, odict()) 82 83 body = error.render() 84 self.assertEqual(body, b'400 Bad Request\n\n\n') 85 body = error.render(jsonify=True) 86 self.assertEqual(body, (b'{\n "status": 400,\n "reason": "Bad Request",\n' 87 b' "title": "",\n "detail":' 88 b' "",\n "fault": null\n}') 89 ) 90 91 error = httping.HTTPError(status=700, 92 title="Validation Error", 93 detail="Bad mojo", 94 fault=50, 95 headers=odict(Accept='application/json')) 96 97 self.assertEqual(error.status, 700) 98 self.assertEqual(error.reason, 'Unknown') 99 self.assertEqual(error.title, "Validation Error") 100 self.assertEqual(error.detail, "Bad mojo") 101 self.assertIs(error.fault, 50) 102 self.assertEqual(error.headers, odict(Accept='application/json')) 103 104 body = error.render() 105 self.assertEqual(body, b'700 Unknown\nValidation Error\nBad mojo\n50') 106 body = error.render(jsonify=True) 107 self.assertEqual(body, (b'{\n "status": 700,\n "reason": "Unknown",\n' 108 b' "title": "Validation Error",' 109 b'\n "detail": "Bad mojo",\n "fault": 50\n}')) 110 111 def testPorterServiceEcho(self): 112 """ 113 Test Porter service request response of echo non blocking 114 """ 115 console.terse("{0}\n".format(self.testPorterServiceEcho.__doc__)) 116 117 118 119 store = storing.Store(stamp=0.0) 120 121 console.terse("{0}\n".format("Building Valet ...\n")) 122 wireLogAlpha = wiring.WireLog(buffify=True, same=True) 123 result = wireLogAlpha.reopen() 124 125 alpha = serving.Porter(port = 6101, 126 bufsize=131072, 127 wlog=wireLogAlpha, 128 store=store) 129 self.assertIs(alpha.servant.reopen(), True) 130 self.assertEqual(alpha.servant.ha, ('0.0.0.0', 6101)) 131 self.assertEqual(alpha.servant.eha, ('127.0.0.1', 6101)) 132 133 console.terse("{0}\n".format("Building Patron ...\n")) 134 wireLogBeta = wiring.WireLog(buffify=True, same=True) 135 result = wireLogBeta.reopen() 136 137 path = "http://{0}:{1}/".format('localhost', alpha.servant.eha[1]) 138 139 beta = clienting.Patron(bufsize=131072, 140 wlog=wireLogBeta, 141 store=store, 142 path=path, 143 reconnectable=True, 144 ) 145 146 self.assertIs(beta.connector.reopen(), True) 147 self.assertIs(beta.connector.accepted, False) 148 self.assertIs(beta.connector.connected, False) 149 self.assertIs(beta.connector.cutoff, False) 150 151 request = odict([('method', u'GET'), 152 ('path', u'/echo?name=fame'), 153 ('qargs', odict()), 154 ('fragment', u''), 155 ('headers', odict([('Accept', 'application/json'), 156 ('Content-Length', 0)])), 157 ]) 158 159 beta.requests.append(request) 160 161 while (beta.requests or beta.connector.txes or not beta.responses or 162 not alpha.servant.ixes or not alpha.idle()): 163 alpha.serviceAll() 164 time.sleep(0.05) 165 beta.serviceAll() 166 time.sleep(0.05) 167 168 self.assertIs(beta.connector.accepted, True) 169 self.assertIs(beta.connector.connected, True) 170 self.assertIs(beta.connector.cutoff, False) 171 172 self.assertEqual(len(alpha.servant.ixes), 1) 173 self.assertEqual(len(alpha.stewards), 1) 174 requestant = alpha.stewards.values()[0].requestant 175 self.assertEqual(requestant.method, request['method']) 176 self.assertEqual(requestant.url, request['path']) 177 self.assertEqual(requestant.headers, {'accept': 'application/json', 178 'accept-encoding': 'identity', 179 'content-length': '0', 180 'host': 'localhost:6101'}) 181 182 183 self.assertEqual(len(beta.responses), 1) 184 response = beta.responses.popleft() 185 self.assertEqual(response['data'],{'body': '', 186 'data': None, 187 'fragment': '', 188 'headers': {'accept': 'application/json', 189 'accept-encoding': 'identity', 190 'content-length': '0', 191 'host': 'localhost:6101'}, 192 'method': 'GET', 193 'path': '/echo', 194 'qargs': {'name': 'fame'}, 195 'version': 'HTTP/1.1'}) 196 197 responder = alpha.stewards.values()[0].responder 198 self.assertEqual(responder.status, response['status']) 199 self.assertEqual(responder.headers, response['headers']) 200 201 alpha.servant.closeAll() 202 beta.connector.close() 203 204 wireLogAlpha.close() 205 wireLogBeta.close() 206 207 def testValetServiceBasic(self): 208 """ 209 Test Valet WSGI service request response 210 """ 211 console.terse("{0}\n".format(self.testValetServiceBasic.__doc__)) 212 213 store = storing.Store(stamp=0.0) 214 215 def wsgiApp(environ, start_response): 216 start_response('200 OK', [('Content-type','text/plain'), 217 ('Content-length', '12')]) 218 return [b"Hello World!"] 219 220 console.terse("{0}\n".format("Building Valet ...\n")) 221 wireLogAlpha = wiring.WireLog(buffify=True, same=True) 222 result = wireLogAlpha.reopen() 223 224 alpha = serving.Valet(port = 6101, 225 bufsize=131072, 226 wlog=wireLogAlpha, 227 store=store, 228 app=wsgiApp) 229 self.assertIs(alpha.servant.reopen(), True) 230 self.assertEqual(alpha.servant.ha, ('0.0.0.0', 6101)) 231 self.assertEqual(alpha.servant.eha, ('127.0.0.1', 6101)) 232 233 console.terse("{0}\n".format("Building Patron ...\n")) 234 wireLogBeta = wiring.WireLog(buffify=True, same=True) 235 result = wireLogBeta.reopen() 236 237 path = "http://{0}:{1}/".format('localhost', alpha.servant.eha[1]) 238 239 beta = clienting.Patron(bufsize=131072, 240 wlog=wireLogBeta, 241 store=store, 242 path=path, 243 reconnectable=True, 244 ) 245 246 self.assertIs(beta.connector.reopen(), True) 247 self.assertIs(beta.connector.accepted, False) 248 self.assertIs(beta.connector.connected, False) 249 self.assertIs(beta.connector.cutoff, False) 250 251 request = odict([('method', u'GET'), 252 ('path', u'/echo?name=fame'), 253 ('qargs', odict()), 254 ('fragment', u''), 255 ('headers', odict([('Accept', 'application/json'), 256 ('Content-Length', 0)])), 257 ]) 258 259 beta.requests.append(request) 260 261 while (beta.requests or beta.connector.txes or not beta.responses or 262 not alpha.idle()): 263 alpha.serviceAll() 264 time.sleep(0.05) 265 beta.serviceAll() 266 time.sleep(0.05) 267 268 self.assertIs(beta.connector.accepted, True) 269 self.assertIs(beta.connector.connected, True) 270 self.assertIs(beta.connector.cutoff, False) 271 272 self.assertEqual(len(alpha.servant.ixes), 1) 273 self.assertEqual(len(alpha.reqs), 1) 274 self.assertEqual(len(alpha.reps), 1) 275 requestant = alpha.reqs.values()[0] 276 self.assertEqual(requestant.method, request['method']) 277 self.assertEqual(requestant.url, request['path']) 278 self.assertEqual(requestant.headers, {'accept': 'application/json', 279 'accept-encoding': 'identity', 280 'content-length': '0', 281 'host': 'localhost:6101'}) 282 283 284 self.assertEqual(len(beta.responses), 1) 285 response = beta.responses.popleft() 286 self.assertEqual(response['body'],bytearray(b'Hello World!')) 287 self.assertEqual(response['status'], 200) 288 289 responder = alpha.reps.values()[0] 290 self.assertTrue(responder.status.startswith, str(response['status'])) 291 self.assertEqual(responder.headers, response['headers']) 292 293 alpha.servant.closeAll() 294 beta.connector.close() 295 296 wireLogAlpha.close() 297 wireLogBeta.close() 298 299 def testValetServiceBottle(self): 300 """ 301 Test Valet WSGI service request response 302 """ 303 console.terse("{0}\n".format(self.testValetServiceBottle.__doc__)) 304 305 try: 306 import bottle 307 except ImportError as ex: 308 console.terse("Bottle not available.\n") 309 return 310 311 store = storing.Store(stamp=0.0) 312 313 app = bottle.default_app() # create bottle app 314 315 @app.get('/echo') 316 @app.get('/echo/<action>') 317 @app.post('/echo') 318 @app.post('/echo/<action>') 319 def echoGet(action=None): 320 """ 321 Echo back request data 322 """ 323 query = dict(bottle.request.query.items()) 324 body = bottle.request.json 325 raw = bottle.request.body.read() 326 form = odict(bottle.request.forms) 327 328 data = odict(verb=bottle.request.method, 329 url=bottle.request.url, 330 action=action, 331 query=query, 332 form=form, 333 content=body) 334 return data 335 336 337 console.terse("{0}\n".format("Building Valet ...\n")) 338 wireLogAlpha = wiring.WireLog(buffify=True, same=True) 339 result = wireLogAlpha.reopen() 340 341 alpha = serving.Valet(port = 6101, 342 bufsize=131072, 343 wlog=wireLogAlpha, 344 store=store, 345 app=app) 346 self.assertIs(alpha.servant.reopen(), True) 347 self.assertEqual(alpha.servant.ha, ('0.0.0.0', 6101)) 348 self.assertEqual(alpha.servant.eha, ('127.0.0.1', 6101)) 349 350 console.terse("{0}\n".format("Building Patron ...\n")) 351 wireLogBeta = wiring.WireLog(buffify=True, same=True) 352 result = wireLogBeta.reopen() 353 354 path = "http://{0}:{1}/".format('localhost', alpha.servant.eha[1]) 355 356 beta = clienting.Patron(bufsize=131072, 357 wlog=wireLogBeta, 358 store=store, 359 path=path, 360 reconnectable=True, 361 ) 362 363 self.assertIs(beta.connector.reopen(), True) 364 self.assertIs(beta.connector.accepted, False) 365 self.assertIs(beta.connector.connected, False) 366 self.assertIs(beta.connector.cutoff, False) 367 368 request = odict([('method', u'GET'), 369 ('path', u'/echo?name=fame'), 370 ('qargs', odict()), 371 ('fragment', u''), 372 ('headers', odict([('Accept', 'application/json'), 373 ('Content-Length', 0)])), 374 ]) 375 376 beta.requests.append(request) 377 timer = StoreTimer(store, duration=1.0) 378 while (beta.requests or beta.connector.txes or not beta.responses or 379 not alpha.idle()): 380 alpha.serviceAll() 381 time.sleep(0.05) 382 beta.serviceAll() 383 time.sleep(0.05) 384 store.advanceStamp(0.1) 385 386 self.assertIs(beta.connector.accepted, True) 387 self.assertIs(beta.connector.connected, True) 388 self.assertIs(beta.connector.cutoff, False) 389 390 self.assertEqual(len(alpha.servant.ixes), 1) 391 self.assertEqual(len(alpha.reqs), 1) 392 self.assertEqual(len(alpha.reps), 1) 393 requestant = alpha.reqs.values()[0] 394 self.assertEqual(requestant.method, request['method']) 395 self.assertEqual(requestant.url, request['path']) 396 self.assertEqual(requestant.headers, {'accept': 'application/json', 397 'accept-encoding': 'identity', 398 'content-length': '0', 399 'host': 'localhost:6101'}) 400 401 self.assertEqual(len(beta.responses), 1) 402 response = beta.responses.popleft() 403 self.assertEqual(response['status'], 200) 404 self.assertEqual(response['reason'], 'OK') 405 self.assertEqual(response['body'], bytearray(b'{"verb": "GET", "url": "http://localhost:6101/echo?name=fame", "' 406 b'action": null, "query": {"name": "fame"}, "form": {}, "content":' 407 b' null}') 408 ) 409 self.assertEqual(response['data'],{'action': None, 410 'content': None, 411 'form': {}, 412 'query': {'name': 'fame'}, 413 'url': 'http://localhost:6101/echo?name=fame', 414 'verb': 'GET'},) 415 416 responder = alpha.reps.values()[0] 417 self.assertTrue(responder.status.startswith, str(response['status'])) 418 self.assertEqual(responder.headers, response['headers']) 419 420 alpha.servant.closeAll() 421 beta.connector.close() 422 423 wireLogAlpha.close() 424 wireLogBeta.close() 425 426 def testValetServiceBottleNoContentLength(self): 427 """ 428 Test Valet WSGI service request response no content-length in request 429 """ 430 console.terse("{0}\n".format(self.testValetServiceBottleNoContentLength.__doc__)) 431 432 try: 433 import bottle 434 except ImportError as ex: 435 console.terse("Bottle not available.\n") 436 return 437 438 store = storing.Store(stamp=0.0) 439 440 app = bottle.default_app() # create bottle app 441 442 @app.get('/echo') 443 @app.get('/echo/<action>') 444 @app.post('/echo') 445 @app.post('/echo/<action>') 446 def echoGet(action=None): 447 """ 448 Echo back request data 449 """ 450 query = dict(bottle.request.query.items()) 451 body = bottle.request.json 452 raw = bottle.request.body.read() 453 form = odict(bottle.request.forms) 454 455 data = odict(verb=bottle.request.method, 456 url=bottle.request.url, 457 action=action, 458 query=query, 459 form=form, 460 content=body) 461 return data 462 463 464 console.terse("{0}\n".format("Building Valet ...\n")) 465 wireLogAlpha = wiring.WireLog(buffify=True, same=True) 466 result = wireLogAlpha.reopen() 467 468 alpha = serving.Valet(port = 6101, 469 bufsize=131072, 470 wlog=wireLogAlpha, 471 store=store, 472 app=app) 473 self.assertIs(alpha.servant.reopen(), True) 474 self.assertEqual(alpha.servant.ha, ('0.0.0.0', 6101)) 475 self.assertEqual(alpha.servant.eha, ('127.0.0.1', 6101)) 476 477 console.terse("{0}\n".format("Building Patron ...\n")) 478 wireLogBeta = wiring.WireLog(buffify=True, same=True) 479 result = wireLogBeta.reopen() 480 481 path = "http://{0}:{1}/".format('localhost', alpha.servant.eha[1]) 482 483 beta = clienting.Patron(bufsize=131072, 484 wlog=wireLogBeta, 485 store=store, 486 path=path, 487 reconnectable=True, 488 ) 489 490 self.assertIs(beta.connector.reopen(), True) 491 self.assertIs(beta.connector.accepted, False) 492 self.assertIs(beta.connector.connected, False) 493 self.assertIs(beta.connector.cutoff, False) 494 495 request = odict([('method', u'GET'), 496 ('path', u'/echo?name=fame'), 497 ('qargs', odict()), 498 ('fragment', u''), 499 ('headers', odict([('Accept', 'application/json'), 500 ])), 501 ]) 502 503 beta.requests.append(request) 504 timer = StoreTimer(store, duration=1.0) 505 while (beta.requests or beta.connector.txes or not beta.responses or 506 not alpha.idle()): 507 alpha.serviceAll() 508 time.sleep(0.05) 509 beta.serviceAll() 510 time.sleep(0.05) 511 store.advanceStamp(0.1) 512 513 self.assertIs(beta.connector.accepted, True) 514 self.assertIs(beta.connector.connected, True) 515 self.assertIs(beta.connector.cutoff, False) 516 517 self.assertEqual(len(alpha.servant.ixes), 1) 518 self.assertEqual(len(alpha.reqs), 1) 519 self.assertEqual(len(alpha.reps), 1) 520 requestant = alpha.reqs.values()[0] 521 self.assertEqual(requestant.method, request['method']) 522 self.assertEqual(requestant.url, request['path']) 523 self.assertEqual(requestant.headers, {'accept': 'application/json', 524 'accept-encoding': 'identity', 525 'host': 'localhost:6101'}) 526 527 self.assertEqual(len(beta.responses), 1) 528 response = beta.responses.popleft() 529 self.assertEqual(response['status'], 200) 530 self.assertEqual(response['reason'], 'OK') 531 self.assertEqual(response['body'], bytearray(b'{"verb": "GET", "url": "http://localhost:6101/echo?name=fame", "' 532 b'action": null, "query": {"name": "fame"}, "form": {}, "content":' 533 b' null}')) 534 self.assertEqual(response['data'],{'action': None, 535 'content': None, 536 'form': {}, 537 'query': {'name': 'fame'}, 538 'url': 'http://localhost:6101/echo?name=fame', 539 'verb': 'GET'},) 540 541 responder = alpha.reps.values()[0] 542 self.assertTrue(responder.status.startswith, str(response['status'])) 543 self.assertEqual(responder.headers, response['headers']) 544 545 alpha.servant.closeAll() 546 beta.connector.close() 547 548 wireLogAlpha.close() 549 wireLogBeta.close() 550 551 def testValetServiceBottleNonPersistent(self): 552 """ 553 Test Valet WSGI service request response non persistent connection in request 554 """ 555 console.terse("{0}\n".format(self.testValetServiceBottleNonPersistent.__doc__)) 556 557 try: 558 import bottle 559 except ImportError as ex: 560 console.terse("Bottle not available.\n") 561 return 562 563 store = storing.Store(stamp=0.0) 564 565 app = bottle.default_app() # create bottle app 566 567 @app.get('/echo') 568 @app.get('/echo/<action>') 569 @app.post('/echo') 570 @app.post('/echo/<action>') 571 def echoGet(action=None): 572 """ 573 Echo back request data 574 """ 575 query = dict(bottle.request.query.items()) 576 body = bottle.request.json 577 raw = bottle.request.body.read() 578 form = odict(bottle.request.forms) 579 580 data = odict(verb=bottle.request.method, 581 url=bottle.request.url, 582 action=action, 583 query=query, 584 form=form, 585 content=body) 586 return data 587 588 589 console.terse("{0}\n".format("Building Valet ...\n")) 590 wireLogAlpha = wiring.WireLog(buffify=True, same=True) 591 result = wireLogAlpha.reopen() 592 593 alpha = serving.Valet(port = 6101, 594 bufsize=131072, 595 wlog=wireLogAlpha, 596 store=store, 597 app=app) 598 self.assertIs(alpha.servant.reopen(), True) 599 self.assertEqual(alpha.servant.ha, ('0.0.0.0', 6101)) 600 self.assertEqual(alpha.servant.eha, ('127.0.0.1', 6101)) 601 602 console.terse("{0}\n".format("Building Patron ...\n")) 603 wireLogBeta = wiring.WireLog(buffify=True, same=True) 604 result = wireLogBeta.reopen() 605 606 path = "http://{0}:{1}/".format('localhost', alpha.servant.eha[1]) 607 608 beta = clienting.Patron(bufsize=131072, 609 wlog=wireLogBeta, 610 store=store, 611 path=path, 612 reconnectable=True, 613 ) 614 615 self.assertIs(beta.connector.reopen(), True) 616 self.assertIs(beta.connector.accepted, False) 617 self.assertIs(beta.connector.connected, False) 618 self.assertIs(beta.connector.cutoff, False) 619 620 request = odict([('method', u'GET'), 621 ('path', u'/echo?name=fame'), 622 ('qargs', odict()), 623 ('fragment', u''), 624 ('headers', odict([('Accept', 'application/json'), 625 ('Connection', 'close')])), 626 ]) 627 628 beta.requests.append(request) 629 timer = StoreTimer(store, duration=1.0) 630 while (beta.requests or beta.connector.txes or not beta.responses or 631 not alpha.idle()): 632 alpha.serviceAll() 633 time.sleep(0.05) 634 beta.serviceAll() 635 time.sleep(0.05) 636 store.advanceStamp(0.1) 637 638 self.assertIs(beta.connector.accepted, True) 639 self.assertIs(beta.connector.connected, True) 640 self.assertIs(beta.connector.cutoff, False) 641 642 self.assertEqual(len(alpha.servant.ixes), 1) 643 self.assertEqual(len(alpha.reqs), 1) 644 self.assertEqual(len(alpha.reps), 1) 645 requestant = alpha.reqs.values()[0] 646 self.assertEqual(requestant.method, request['method']) 647 self.assertEqual(requestant.url, request['path']) 648 self.assertEqual(requestant.headers, {'accept': 'application/json', 649 'accept-encoding': 'identity', 650 'host': 'localhost:6101', 651 'connection': 'close',}) 652 653 self.assertEqual(len(beta.responses), 1) 654 response = beta.responses.popleft() 655 self.assertEqual(response['status'], 200) 656 self.assertEqual(response['reason'], 'OK') 657 self.assertEqual(response['body'], bytearray(b'{"verb": "GET", "url": "http://localhost:6101/echo?name=fame", "' 658 b'action": null, "query": {"name": "fame"}, "form": {}, "content":' 659 b' null}')) 660 self.assertEqual(response['data'],{'action': None, 661 'content': None, 662 'form': {}, 663 'query': {'name': 'fame'}, 664 'url': 'http://localhost:6101/echo?name=fame', 665 'verb': 'GET'},) 666 667 responder = alpha.reps.values()[0] 668 self.assertTrue(responder.status.startswith, str(response['status'])) 669 self.assertEqual(responder.headers, response['headers']) 670 671 alpha.servant.closeAll() 672 beta.connector.close() 673 674 wireLogAlpha.close() 675 wireLogBeta.close() 676 677 def testValetServiceBottleStream(self): 678 """ 679 Test Valet WSGI service request response stream sse 680 """ 681 console.terse("{0}\n".format(self.testValetServiceBottleStream.__doc__)) 682 683 try: 684 import bottle 685 except ImportError as ex: 686 console.terse("Bottle not available.\n") 687 return 688 689 store = storing.Store(stamp=0.0) 690 691 app = bottle.default_app() # create bottle app 692 693 @app.get('/stream') 694 def streamGet(): 695 """ 696 Create test server sent event stream that sends count events 697 """ 698 timer = StoreTimer(store, duration=2.0) 699 bottle.response.set_header('Content-Type', 'text/event-stream') #text 700 bottle.response.set_header('Cache-Control', 'no-cache') 701 # HTTP 1.1 servers detect text/event-stream and use Transfer-Encoding: chunked 702 # Set client-side auto-reconnect timeout, ms. 703 yield 'retry: 1000\n\n' 704 i = 0 705 yield 'id: {0}\n'.format(i) 706 i += 1 707 yield 'data: START\n\n' 708 n = 1 709 while not timer.expired: 710 yield 'id: {0}\n'.format(i) 711 i += 1 712 yield 'data: {0}\n\n'.format(n) 713 n += 1 714 yield "data: END\n\n" 715 716 console.terse("{0}\n".format("Building Valet ...\n")) 717 wireLogAlpha = wiring.WireLog(buffify=True, same=True) 718 result = wireLogAlpha.reopen() 719 720 alpha = serving.Valet(port = 6101, 721 bufsize=131072, 722 wlog=wireLogAlpha, 723 store=store, 724 app=app) 725 self.assertIs(alpha.servant.reopen(), True) 726 self.assertEqual(alpha.servant.ha, ('0.0.0.0', 6101)) 727 self.assertEqual(alpha.servant.eha, ('127.0.0.1', 6101)) 728 729 console.terse("{0}\n".format("Building Patron ...\n")) 730 wireLogBeta = wiring.WireLog(buffify=True, same=True) 731 result = wireLogBeta.reopen() 732 733 path = "http://{0}:{1}/".format('localhost', alpha.servant.eha[1]) 734 735 beta = clienting.Patron(bufsize=131072, 736 wlog=wireLogBeta, 737 store=store, 738 path=path, 739 reconnectable=True, 740 ) 741 742 self.assertIs(beta.connector.reopen(), True) 743 self.assertIs(beta.connector.accepted, False) 744 self.assertIs(beta.connector.connected, False) 745 self.assertIs(beta.connector.cutoff, False) 746 747 request = odict([('method', u'GET'), 748 ('path', u'/stream'), 749 ('qargs', odict()), 750 ('fragment', u''), 751 ('headers', odict([('Accept', 'application/json'), 752 ('Content-Length', 0)])), 753 ('body', None), 754 ]) 755 756 beta.requests.append(request) 757 timer = StoreTimer(store, duration=1.0) 758 while (not timer.expired): 759 alpha.serviceAll() 760 time.sleep(0.05) 761 beta.serviceAll() 762 time.sleep(0.05) 763 store.advanceStamp(0.1) 764 765 self.assertIs(beta.connector.accepted, True) 766 self.assertIs(beta.connector.connected, True) 767 self.assertIs(beta.connector.cutoff, False) 768 769 self.assertEqual(len(alpha.servant.ixes), 1) 770 self.assertEqual(len(alpha.reqs), 1) 771 self.assertEqual(len(alpha.reps), 1) 772 requestant = alpha.reqs.values()[0] 773 self.assertEqual(requestant.method, request['method']) 774 self.assertEqual(requestant.url, request['path']) 775 self.assertEqual(requestant.headers, {'accept': 'application/json', 776 'accept-encoding': 'identity', 777 'content-length': '0', 778 'host': 'localhost:6101'}) 779 780 781 #timed out while stream still open so no responses in .responses 782 self.assertIs(beta.waited, True) 783 self.assertIs(beta.respondent.ended, False) 784 self.assertEqual(len(beta.responses), 0) 785 self.assertIn('content-type', beta.respondent.headers) 786 self.assertEqual(beta.respondent.headers['content-type'], 'text/event-stream') 787 self.assertIn('transfer-encoding', beta.respondent.headers) 788 self.assertEqual(beta.respondent.headers['transfer-encoding'], 'chunked') 789 790 self.assertTrue(len(beta.events) >= 3) 791 self.assertEqual(beta.respondent.retry, 1000) 792 self.assertTrue(int(beta.respondent.leid) >= 2) 793 event = beta.events.popleft() 794 self.assertEqual(event, {'id': '0', 'name': '', 'data': 'START'}) 795 event = beta.events.popleft() 796 self.assertEqual(event, {'id': '1', 'name': '', 'data': '1'}) 797 event = beta.events.popleft() 798 self.assertEqual(event, {'id': '2', 'name': '', 'data': '2'}) 799 beta.events.clear() 800 801 #keep going until ended 802 timer.restart(duration=1.5) 803 while (not timer.expired): 804 alpha.serviceAll() 805 time.sleep(0.05) 806 beta.serviceAll() 807 time.sleep(0.05) 808 store.advanceStamp(0.1) 809 810 self.assertTrue(len(beta.events) >= 3) 811 self.assertEqual(beta.respondent.leid, '9') 812 self.assertEqual(beta.events[-2], {'id': '9', 'name': '', 'data': '9'}) 813 self.assertEqual(beta.events[-1], {'id': '9', 'name': '', 'data': 'END'}) 814 beta.events.clear() 815 816 alpha.servant.closeAll() 817 beta.connector.close() 818 819 wireLogAlpha.close() 820 wireLogBeta.close() 821 822 def testValetServiceBasicSecure(self): 823 """ 824 Test Valet WSGI service with secure TLS request response 825 """ 826 console.terse("{0}\n".format(self.testValetServiceBasicSecure.__doc__)) 827 828 store = storing.Store(stamp=0.0) 829 830 def wsgiApp(environ, start_response): 831 start_response('200 OK', [('Content-type','text/plain'), 832 ('Content-length', '12')]) 833 return [b"Hello World!"] 834 835 console.terse("{0}\n".format("Building Valet ...\n")) 836 wireLogAlpha = wiring.WireLog(buffify=True, same=True) 837 result = wireLogAlpha.reopen() 838 839 serverCertCommonName = 'localhost' # match hostname uses servers's cert commonname 840 #serverKeypath = '/etc/pki/tls/certs/server_key.pem' # local server private key 841 #serverCertpath = '/etc/pki/tls/certs/server_cert.pem' # local server public cert 842 #clientCafilepath = '/etc/pki/tls/certs/client.pem' # remote client public cert 843 844 serverKeypath = self.certdirpath + '/server_key.pem' # local server private key 845 serverCertpath = self.certdirpath + '/server_cert.pem' # local server public cert 846 clientCafilepath = self.certdirpath + '/client.pem' # remote client public cert 847 848 alpha = serving.Valet(port = 6101, 849 bufsize=131072, 850 wlog=wireLogAlpha, 851 store=store, 852 app=wsgiApp, 853 scheme='https', 854 keypath=serverKeypath, 855 certpath=serverCertpath, 856 cafilepath=clientCafilepath,) 857 858 self.assertIs(alpha.servant.reopen(), True) 859 self.assertEqual(alpha.servant.ha, ('0.0.0.0', 6101)) 860 self.assertEqual(alpha.servant.eha, ('127.0.0.1', 6101)) 861 862 console.terse("{0}\n".format("Building Patron ...\n")) 863 wireLogBeta = wiring.WireLog(buffify=True, same=True) 864 result = wireLogBeta.reopen() 865 866 #clientKeypath = '/etc/pki/tls/certs/client_key.pem' # local client private key 867 #clientCertpath = '/etc/pki/tls/certs/client_cert.pem' # local client public cert 868 #serverCafilepath = '/etc/pki/tls/certs/server.pem' # remote server public cert 869 870 clientKeypath = self.certdirpath + '/client_key.pem' # local client private key 871 clientCertpath = self.certdirpath + '/client_cert.pem' # local client public cert 872 serverCafilepath = self.certdirpath + '/server.pem' # remote server public cert 873 874 path = "https://{0}:{1}/".format('localhost', alpha.servant.eha[1]) 875 876 beta = clienting.Patron(bufsize=131072, 877 wlog=wireLogBeta, 878 store=store, 879 path=path, 880 reconnectable=True, 881 scheme='https', 882 certedhost=serverCertCommonName, 883 keypath=clientKeypath, 884 certpath=clientCertpath, 885 cafilepath=serverCafilepath 886 ) 887 888 self.assertIs(beta.connector.reopen(), True) 889 self.assertIs(beta.connector.accepted, False) 890 self.assertIs(beta.connector.connected, False) 891 self.assertIs(beta.connector.cutoff, False) 892 893 request = odict([('method', u'GET'), 894 ('path', u'/echo?name=fame'), 895 ('qargs', odict()), 896 ('fragment', u''), 897 ('headers', odict([('Accept', 'application/json'), 898 ('Content-Length', 0)])), 899 ]) 900 901 beta.requests.append(request) 902 903 while (beta.requests or beta.connector.txes or not beta.responses or 904 not alpha.idle()): 905 alpha.serviceAll() 906 time.sleep(0.05) 907 beta.serviceAll() 908 time.sleep(0.05) 909 910 self.assertIs(beta.connector.accepted, True) 911 self.assertIs(beta.connector.connected, True) 912 self.assertIs(beta.connector.cutoff, False) 913 914 self.assertEqual(len(alpha.servant.ixes), 1) 915 self.assertEqual(len(alpha.reqs), 1) 916 self.assertEqual(len(alpha.reps), 1) 917 requestant = alpha.reqs.values()[0] 918 self.assertEqual(requestant.method, request['method']) 919 self.assertEqual(requestant.url, request['path']) 920 self.assertEqual(requestant.headers, {'accept': 'application/json', 921 'accept-encoding': 'identity', 922 'content-length': '0', 923 'host': 'localhost:6101'}) 924 925 926 self.assertEqual(len(beta.responses), 1) 927 response = beta.responses.popleft() 928 self.assertEqual(response['body'],bytearray(b'Hello World!')) 929 self.assertEqual(response['status'], 200) 930 931 responder = alpha.reps.values()[0] 932 self.assertTrue(responder.status.startswith, str(response['status'])) 933 self.assertEqual(responder.headers, response['headers']) 934 935 alpha.servant.closeAll() 936 beta.connector.close() 937 938 wireLogAlpha.close() 939 wireLogBeta.close() 940 941 def testValetServiceBottleSecure(self): 942 """ 943 Test Valet WSGI service secure TLS request response 944 """ 945 console.terse("{0}\n".format(self.testValetServiceBottleSecure.__doc__)) 946 947 try: 948 import bottle 949 except ImportError as ex: 950 console.terse("Bottle not available.\n") 951 return 952 953 store = storing.Store(stamp=0.0) 954 955 app = bottle.default_app() # create bottle app 956 957 @app.get('/echo') 958 @app.get('/echo/<action>') 959 @app.post('/echo') 960 @app.post('/echo/<action>') 961 def echoGet(action=None): 962 """ 963 Echo back request data 964 """ 965 query = dict(bottle.request.query.items()) 966 body = bottle.request.json 967 raw = bottle.request.body.read() 968 form = odict(bottle.request.forms) 969 970 data = odict(verb=bottle.request.method, 971 url=bottle.request.url, 972 action=action, 973 query=query, 974 form=form, 975 content=body) 976 return data 977 978 979 console.terse("{0}\n".format("Building Valet ...\n")) 980 wireLogAlpha = wiring.WireLog(buffify=True, same=True) 981 result = wireLogAlpha.reopen() 982 983 serverCertCommonName = 'localhost' # match hostname uses servers's cert commonname 984 #serverKeypath = '/etc/pki/tls/certs/server_key.pem' # local server private key 985 #serverCertpath = '/etc/pki/tls/certs/server_cert.pem' # local server public cert 986 #clientCafilepath = '/etc/pki/tls/certs/client.pem' # remote client public cert 987 988 serverKeypath = self.certdirpath + '/server_key.pem' # local server private key 989 serverCertpath = self.certdirpath + '/server_cert.pem' # local server public cert 990 clientCafilepath = self.certdirpath + '/client.pem' # remote client public cert 991 992 alpha = serving.Valet(port = 6101, 993 bufsize=131072, 994 wlog=wireLogAlpha, 995 store=store, 996 app=app, 997 scheme='https', 998 keypath=serverKeypath, 999 certpath=serverCertpath, 1000 cafilepath=clientCafilepath, 1001 ) 1002 self.assertIs(alpha.servant.reopen(), True) 1003 self.assertEqual(alpha.servant.ha, ('0.0.0.0', 6101)) 1004 self.assertEqual(alpha.servant.eha, ('127.0.0.1', 6101)) 1005 1006 console.terse("{0}\n".format("Building Patron ...\n")) 1007 wireLogBeta = wiring.WireLog(buffify=True, same=True) 1008 result = wireLogBeta.reopen() 1009 1010 #clientKeypath = '/etc/pki/tls/certs/client_key.pem' # local client private key 1011 #clientCertpath = '/etc/pki/tls/certs/client_cert.pem' # local client public cert 1012 #serverCafilepath = '/etc/pki/tls/certs/server.pem' # remote server public cert 1013 1014 clientKeypath = self.certdirpath + '/client_key.pem' # local client private key 1015 clientCertpath = self.certdirpath + '/client_cert.pem' # local client public cert 1016 serverCafilepath = self.certdirpath + '/server.pem' # remote server public cert 1017 1018 path = "https://{0}:{1}/".format('localhost', alpha.servant.eha[1]) 1019 1020 beta = clienting.Patron(bufsize=131072, 1021 wlog=wireLogBeta, 1022 store=store, 1023 path=path, 1024 reconnectable=True, 1025 scheme='https', 1026 certedhost=serverCertCommonName, 1027 keypath=clientKeypath, 1028 certpath=clientCertpath, 1029 cafilepath=serverCafilepath 1030 ) 1031 1032 self.assertIs(beta.connector.reopen(), True) 1033 self.assertIs(beta.connector.accepted, False) 1034 self.assertIs(beta.connector.connected, False) 1035 self.assertIs(beta.connector.cutoff, False) 1036 1037 request = odict([('method', u'GET'), 1038 ('path', u'/echo?name=fame'), 1039 ('qargs', odict()), 1040 ('fragment', u''), 1041 ('headers', odict([('Accept', 'application/json'), 1042 ('Content-Length', 0)])), 1043 ]) 1044 1045 beta.requests.append(request) 1046 timer = StoreTimer(store, duration=1.0) 1047 while (beta.requests or beta.connector.txes or not beta.responses or 1048 not alpha.idle()): 1049 alpha.serviceAll() 1050 time.sleep(0.05) 1051 beta.serviceAll() 1052 time.sleep(0.05) 1053 store.advanceStamp(0.1) 1054 1055 self.assertIs(beta.connector.accepted, True) 1056 self.assertIs(beta.connector.connected, True) 1057 self.assertIs(beta.connector.cutoff, False) 1058 1059 self.assertEqual(len(alpha.servant.ixes), 1) 1060 self.assertEqual(len(alpha.reqs), 1) 1061 self.assertEqual(len(alpha.reps), 1) 1062 requestant = alpha.reqs.values()[0] 1063 self.assertEqual(requestant.method, request['method']) 1064 self.assertEqual(requestant.url, request['path']) 1065 self.assertEqual(requestant.headers, {'accept': 'application/json', 1066 'accept-encoding': 'identity', 1067 'content-length': '0', 1068 'host': 'localhost:6101'}) 1069 1070 self.assertEqual(len(beta.responses), 1) 1071 response = beta.responses.popleft() 1072 self.assertEqual(response['status'], 200) 1073 self.assertEqual(response['reason'], 'OK') 1074 self.assertEqual(response['body'], bytearray(b'{"verb": "GET", "url": "https://localhost:6101/echo?name=fame", ' 1075 b'"action": null, "query": {"name": "fame"}, "form": {}, "content"' 1076 b': null}')) 1077 self.assertEqual(response['data'],{'action': None, 1078 'content': None, 1079 'form': {}, 1080 'query': {'name': 'fame'}, 1081 'url': 'https://localhost:6101/echo?name=fame', 1082 'verb': 'GET'},) 1083 1084 responder = alpha.reps.values()[0] 1085 self.assertTrue(responder.status.startswith, str(response['status'])) 1086 self.assertEqual(responder.headers, response['headers']) 1087 1088 alpha.servant.closeAll() 1089 beta.connector.close() 1090 1091 wireLogAlpha.close() 1092 wireLogBeta.close() 1093 1094 def testValetServiceBottleStreamSecure(self): 1095 """ 1096 Test Valet WSGI service request response stream sse 1097 """ 1098 console.terse("{0}\n".format(self.testValetServiceBottleStreamSecure.__doc__)) 1099 1100 try: 1101 import bottle 1102 except ImportError as ex: 1103 console.terse("Bottle not available.\n") 1104 return 1105 1106 store = storing.Store(stamp=0.0) 1107 1108 app = bottle.default_app() # create bottle app 1109 1110 @app.get('/stream') 1111 def streamGet(): 1112 """ 1113 Create test server sent event stream that sends count events 1114 """ 1115 timer = StoreTimer(store, duration=2.0) 1116 bottle.response.set_header('Content-Type', 'text/event-stream') #text 1117 bottle.response.set_header('Cache-Control', 'no-cache') 1118 # HTTP 1.1 servers detect text/event-stream and use Transfer-Encoding: chunked 1119 # Set client-side auto-reconnect timeout, ms. 1120 yield 'retry: 1000\n\n' 1121 i = 0 1122 yield 'id: {0}\n'.format(i) 1123 i += 1 1124 yield 'data: START\n\n' 1125 n = 1 1126 while not timer.expired: 1127 yield 'id: {0}\n'.format(i) 1128 i += 1 1129 yield 'data: {0}\n\n'.format(n) 1130 n += 1 1131 yield "data: END\n\n" 1132 1133 console.terse("{0}\n".format("Building Valet ...\n")) 1134 wireLogAlpha = wiring.WireLog(buffify=True, same=True) 1135 result = wireLogAlpha.reopen() 1136 1137 serverCertCommonName = 'localhost' # match hostname uses servers's cert commonname 1138 #serverKeypath = '/etc/pki/tls/certs/server_key.pem' # local server private key 1139 #serverCertpath = '/etc/pki/tls/certs/server_cert.pem' # local server public cert 1140 #clientCafilepath = '/etc/pki/tls/certs/client.pem' # remote client public cert 1141 1142 serverKeypath = self.certdirpath + '/server_key.pem' # local server private key 1143 serverCertpath = self.certdirpath + '/server_cert.pem' # local server public cert 1144 clientCafilepath = self.certdirpath + '/client.pem' # remote client public cert 1145 1146 alpha = serving.Valet(port = 6101, 1147 bufsize=131072, 1148 wlog=wireLogAlpha, 1149 store=store, 1150 app=app, 1151 scheme='https', 1152 keypath=serverKeypath, 1153 certpath=serverCertpath, 1154 cafilepath=clientCafilepath, 1155 ) 1156 self.assertIs(alpha.servant.reopen(), True) 1157 self.assertEqual(alpha.servant.ha, ('0.0.0.0', 6101)) 1158 self.assertEqual(alpha.servant.eha, ('127.0.0.1', 6101)) 1159 1160 console.terse("{0}\n".format("Building Patron ...\n")) 1161 wireLogBeta = wiring.WireLog(buffify=True, same=True) 1162 result = wireLogBeta.reopen() 1163 1164 #clientKeypath = '/etc/pki/tls/certs/client_key.pem' # local client private key 1165 #clientCertpath = '/etc/pki/tls/certs/client_cert.pem' # local client public cert 1166 #serverCafilepath = '/etc/pki/tls/certs/server.pem' # remote server public cert 1167 1168 clientKeypath = self.certdirpath + '/client_key.pem' # local client private key 1169 clientCertpath = self.certdirpath + '/client_cert.pem' # local client public cert 1170 serverCafilepath = self.certdirpath + '/server.pem' # remote server public cert 1171 1172 path = "https://{0}:{1}/".format('localhost', alpha.servant.eha[1]) 1173 1174 beta = clienting.Patron(bufsize=131072, 1175 wlog=wireLogBeta, 1176 store=store, 1177 path=path, 1178 reconnectable=True, 1179 scheme='https', 1180 certedhost=serverCertCommonName, 1181 keypath=clientKeypath, 1182 certpath=clientCertpath, 1183 cafilepath=serverCafilepath 1184 ) 1185 1186 self.assertIs(beta.connector.reopen(), True) 1187 self.assertIs(beta.connector.accepted, False) 1188 self.assertIs(beta.connector.connected, False) 1189 self.assertIs(beta.connector.cutoff, False) 1190 1191 request = odict([('method', u'GET'), 1192 ('path', u'/stream'), 1193 ('qargs', odict()), 1194 ('fragment', u''), 1195 ('headers', odict([('Accept', 'application/json'), 1196 ('Content-Length', 0)])), 1197 ('body', None), 1198 ]) 1199 1200 beta.requests.append(request) 1201 timer = StoreTimer(store, duration=1.0) 1202 while (not timer.expired): 1203 alpha.serviceAll() 1204 time.sleep(0.05) 1205 beta.serviceAll() 1206 time.sleep(0.05) 1207 store.advanceStamp(0.1) 1208 1209 self.assertIs(beta.connector.accepted, True) 1210 self.assertIs(beta.connector.connected, True) 1211 self.assertIs(beta.connector.cutoff, False) 1212 1213 self.assertEqual(len(alpha.servant.ixes), 1) 1214 self.assertEqual(len(alpha.reqs), 1) 1215 self.assertEqual(len(alpha.reps), 1) 1216 requestant = alpha.reqs.values()[0] 1217 self.assertEqual(requestant.method, request['method']) 1218 self.assertEqual(requestant.url, request['path']) 1219 self.assertEqual(requestant.headers, {'accept': 'application/json', 1220 'accept-encoding': 'identity', 1221 'content-length': '0', 1222 'host': 'localhost:6101'}) 1223 1224 1225 #timed out while stream still open so no responses in .responses 1226 self.assertIs(beta.waited, True) 1227 self.assertIs(beta.respondent.ended, False) 1228 self.assertEqual(len(beta.responses), 0) 1229 self.assertIn('content-type', beta.respondent.headers) 1230 self.assertEqual(beta.respondent.headers['content-type'], 'text/event-stream') 1231 self.assertIn('transfer-encoding', beta.respondent.headers) 1232 self.assertEqual(beta.respondent.headers['transfer-encoding'], 'chunked') 1233 1234 self.assertTrue(len(beta.events) >= 3) 1235 self.assertEqual(beta.respondent.retry, 1000) 1236 self.assertTrue(int(beta.respondent.leid) >= 2) 1237 event = beta.events.popleft() 1238 self.assertEqual(event, {'id': '0', 'name': '', 'data': 'START'}) 1239 event = beta.events.popleft() 1240 self.assertEqual(event, {'id': '1', 'name': '', 'data': '1'}) 1241 event = beta.events.popleft() 1242 self.assertEqual(event, {'id': '2', 'name': '', 'data': '2'}) 1243 beta.events.clear() 1244 1245 #keep going until ended 1246 timer.restart(duration=1.5) 1247 while (not timer.expired): 1248 alpha.serviceAll() 1249 time.sleep(0.05) 1250 beta.serviceAll() 1251 time.sleep(0.05) 1252 store.advanceStamp(0.1) 1253 1254 self.assertTrue(len(beta.events) >= 3) 1255 self.assertEqual(beta.respondent.leid, '9') 1256 self.assertEqual(beta.events[-2], {'id': '9', 'name': '', 'data': '9'}) 1257 self.assertEqual(beta.events[-1], {'id': '9', 'name': '', 'data': 'END'}) 1258 beta.events.clear() 1259 1260 alpha.servant.closeAll() 1261 beta.connector.close() 1262 1263 wireLogAlpha.close() 1264 wireLogBeta.close() 1265 1266 1267def runOne(test): 1268 ''' 1269 Unittest Runner 1270 ''' 1271 test = BasicTestCase(test) 1272 suite = unittest.TestSuite([test]) 1273 unittest.TextTestRunner(verbosity=2).run(suite) 1274 1275def runSome(): 1276 """ Unittest runner """ 1277 tests = [] 1278 names = [ 1279 'testHttpError', 1280 'testPorterServiceEcho', 1281 'testValetServiceBasic', 1282 'testValetServiceBottle', 1283 'testValetServiceBottleNoContentLength', 1284 'testValetServiceBottleNonPersistent', 1285 'testValetServiceBottleStream', 1286 'testValetServiceBasicSecure', 1287 'testValetServiceBottleSecure', 1288 'testValetServiceBottleStreamSecure', 1289 ] 1290 tests.extend(map(BasicTestCase, names)) 1291 suite = unittest.TestSuite(tests) 1292 unittest.TextTestRunner(verbosity=2).run(suite) 1293 1294def runAll(): 1295 """ Unittest runner """ 1296 suite = unittest.TestSuite() 1297 suite.addTest(unittest.TestLoader().loadTestsFromTestCase(BasicTestCase)) 1298 unittest.TextTestRunner(verbosity=2).run(suite) 1299 1300if __name__ == '__main__' and __package__ is None: 1301 1302 # 1303 1304 #runAll() #run all unittests 1305 1306 runSome()#only run some 1307 1308 #runOne('testValetServiceBottle') 1309 #runOne('testValetServiceBottleStreamSecure') 1310 #runOne('testHttpError') 1311 #runOne('testValetServiceBottleStream') 1312