Package web2py :: Package gluon :: Module main
[hide private]
[frames] | no frames]

Source Code for Module web2py.gluon.main

  1  #!/bin/env python 
  2  # -*- coding: utf-8 -*- 
  3   
  4  """ 
  5  This file is part of the web2py Web Framework 
  6  Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu> 
  7  License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html) 
  8   
  9  Contains: 
 10   
 11  - wsgibase: the gluon wsgi application 
 12   
 13  """ 
 14   
 15  import gc 
 16  import cgi 
 17  import cStringIO 
 18  import Cookie 
 19  import os 
 20  import re 
 21  import copy 
 22  import sys 
 23  import time 
 24  import thread 
 25  import datetime 
 26  import signal 
 27  import socket 
 28  import tempfile 
 29  import random 
 30  import string 
 31  import platform 
 32  from fileutils import abspath, write_file 
 33  from settings import global_settings 
 34  from admin import add_path_first, create_missing_folders, create_missing_app_folders 
 35  from globals import current 
 36   
 37  from custom_import import custom_import_install 
 38  from contrib.simplejson import dumps 
 39   
 40  if platform.system=='Windows': 
 41      os.environ["PYTHONCASEOK"]="1" 
 42   
 43  #  Remarks: 
 44  #  calling script has inserted path to script directory into sys.path 
 45  #  applications_parent (path to applications/, site-packages/ etc) 
 46  #  defaults to that directory set sys.path to 
 47  #  ("", gluon_parent/site-packages, gluon_parent, ...) 
 48  # 
 49  #  this is wrong: 
 50  #  web2py_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 
 51  #  because we do not want the path to this file which may be Library.zip 
 52  #  gluon_parent is the directory containing gluon, web2py.py, logging.conf 
 53  #  and the handlers. 
 54  #  applications_parent (web2py_path) is the directory containing applications/ 
 55  #  and routes.py 
 56  #  The two are identical unless web2py_path is changed via the web2py.py -f folder option 
 57  #  main.web2py_path is the same as applications_parent (for backward compatibility) 
 58   
 59  if not hasattr(os, 'mkdir'): 
 60      global_settings.db_sessions = True 
 61  if global_settings.db_sessions is not True: 
 62      global_settings.db_sessions = set() 
 63  global_settings.gluon_parent = os.environ.get('web2py_path', os.getcwd()) 
 64  global_settings.applications_parent = global_settings.gluon_parent 
 65  web2py_path = global_settings.applications_parent # backward compatibility 
 66  global_settings.app_folders = set() 
 67  global_settings.debugging = False 
 68   
 69  custom_import_install(web2py_path) 
 70   
 71  create_missing_folders() 
 72   
 73  # set up logging for subsequent imports 
 74  import logging 
 75  import logging.config 
 76  logpath = abspath("logging.conf") 
 77  if os.path.exists(logpath): 
 78      logging.config.fileConfig(abspath("logging.conf")) 
 79  else: 
 80      logging.basicConfig() 
 81  logger = logging.getLogger("web2py") 
 82   
 83  from restricted import RestrictedError 
 84  from http import HTTP, redirect 
 85  from globals import Request, Response, Session 
 86  from compileapp import build_environment, run_models_in, \ 
 87      run_controller_in, run_view_in 
 88  from fileutils import copystream 
 89  from contenttype import contenttype 
 90  from dal import BaseAdapter 
 91  from settings import global_settings 
 92  from validators import CRYPT 
 93  from cache import Cache 
 94  from html import URL as Url 
 95  import newcron 
 96  import rewrite 
 97   
 98  __all__ = ['wsgibase', 'save_password', 'appfactory', 'HttpServer'] 
 99   
100  requests = 0    # gc timer 
101   
102  # Security Checks: validate URL and session_id here, 
103  # accept_language is validated in languages 
104   
105  # pattern used to validate client address 
106  regex_client = re.compile('[\w\-:]+(\.[\w\-]+)*\.?')  # ## to account for IPV6 
107   
108  version_info = open(abspath('VERSION', gluon=True), 'r') 
109  web2py_version = version_info.read() 
110  version_info.close() 
111   
112  try: 
113      import rocket 
114  except: 
115      if not global_settings.web2py_runtime_gae: 
116          logger.warn('unable to import Rocket') 
117   
118  rewrite.load() 
119   
120 -def get_client(env):
121 """ 122 guess the client address from the environment variables 123 124 first tries 'http_x_forwarded_for', secondly 'remote_addr' 125 if all fails assume '127.0.0.1' (running locally) 126 """ 127 g = regex_client.search(env.get('http_x_forwarded_for', '')) 128 if g: 129 return g.group() 130 g = regex_client.search(env.get('remote_addr', '')) 131 if g: 132 return g.group() 133 return '127.0.0.1'
134
135 -def copystream_progress(request, chunk_size= 10**5):
136 """ 137 copies request.env.wsgi_input into request.body 138 and stores progress upload status in cache.ram 139 X-Progress-ID:length and X-Progress-ID:uploaded 140 """ 141 if not request.env.content_length: 142 return cStringIO.StringIO() 143 source = request.env.wsgi_input 144 size = int(request.env.content_length) 145 dest = tempfile.TemporaryFile() 146 if not 'X-Progress-ID' in request.vars: 147 copystream(source, dest, size, chunk_size) 148 return dest 149 cache_key = 'X-Progress-ID:'+request.vars['X-Progress-ID'] 150 cache = Cache(request) 151 cache.ram(cache_key+':length', lambda: size, 0) 152 cache.ram(cache_key+':uploaded', lambda: 0, 0) 153 while size > 0: 154 if size < chunk_size: 155 data = source.read(size) 156 cache.ram.increment(cache_key+':uploaded', size) 157 else: 158 data = source.read(chunk_size) 159 cache.ram.increment(cache_key+':uploaded', chunk_size) 160 length = len(data) 161 if length > size: 162 (data, length) = (data[:size], size) 163 size -= length 164 if length == 0: 165 break 166 dest.write(data) 167 if length < chunk_size: 168 break 169 dest.seek(0) 170 cache.ram(cache_key+':length', None) 171 cache.ram(cache_key+':uploaded', None) 172 return dest
173 174
175 -def serve_controller(request, response, session):
176 """ 177 this function is used to generate a dynamic page. 178 It first runs all models, then runs the function in the controller, 179 and then tries to render the output using a view/template. 180 this function must run from the [application] folder. 181 A typical example would be the call to the url 182 /[application]/[controller]/[function] that would result in a call 183 to [function]() in applications/[application]/[controller].py 184 rendered by applications/[application]/views/[controller]/[function].html 185 """ 186 187 # ################################################## 188 # build environment for controller and view 189 # ################################################## 190 191 environment = build_environment(request, response, session) 192 193 # set default view, controller can override it 194 195 response.view = '%s/%s.%s' % (request.controller, 196 request.function, 197 request.extension) 198 199 # also, make sure the flash is passed through 200 # ################################################## 201 # process models, controller and view (if required) 202 # ################################################## 203 204 run_models_in(environment) 205 response._view_environment = copy.copy(environment) 206 page = run_controller_in(request.controller, request.function, environment) 207 if isinstance(page, dict): 208 response._vars = page 209 for key in page: 210 response._view_environment[key] = page[key] 211 run_view_in(response._view_environment) 212 page = response.body.getvalue() 213 # logic to garbage collect after exec, not always, once every 100 requests 214 global requests 215 requests = ('requests' in globals()) and (requests+1) % 100 or 0 216 if not requests: gc.collect() 217 # end garbage collection logic 218 raise HTTP(response.status, page, **response.headers)
219 220
221 -def start_response_aux(status, headers, exc_info, response=None):
222 """ 223 in controller you can use:: 224 225 - request.wsgi.environ 226 - request.wsgi.start_response 227 228 to call third party WSGI applications 229 """ 230 response.status = str(status).split(' ',1)[0] 231 response.headers = dict(headers) 232 return lambda *args, **kargs: response.write(escape=False,*args,**kargs)
233 234
235 -def middleware_aux(request, response, *middleware_apps):
236 """ 237 In you controller use:: 238 239 @request.wsgi.middleware(middleware1, middleware2, ...) 240 241 to decorate actions with WSGI middleware. actions must return strings. 242 uses a simulated environment so it may have weird behavior in some cases 243 """ 244 def middleware(f): 245 def app(environ, start_response): 246 data = f() 247 start_response(response.status,response.headers.items()) 248 if isinstance(data,list): 249 return data 250 return [data]
251 for item in middleware_apps: 252 app=item(app) 253 def caller(app): 254 return app(request.wsgi.environ,request.wsgi.start_response) 255 return lambda caller=caller, app=app: caller(app) 256 return middleware 257
258 -def environ_aux(environ,request):
259 new_environ = copy.copy(environ) 260 new_environ['wsgi.input'] = request.body 261 new_environ['wsgi.version'] = 1 262 return new_environ
263
264 -def parse_get_post_vars(request, environ):
265 266 # always parse variables in URL for GET, POST, PUT, DELETE, etc. in get_vars 267 dget = cgi.parse_qsl(request.env.query_string or '', keep_blank_values=1) 268 for (key, value) in dget: 269 if key in request.get_vars: 270 if isinstance(request.get_vars[key], list): 271 request.get_vars[key] += [value] 272 else: 273 request.get_vars[key] = [request.get_vars[key]] + [value] 274 else: 275 request.get_vars[key] = value 276 request.vars[key] = request.get_vars[key] 277 278 # parse POST variables on POST, PUT, BOTH only in post_vars 279 request.body = copystream_progress(request) ### stores request body 280 if (request.body and request.env.request_method in ('POST', 'PUT', 'BOTH')): 281 dpost = cgi.FieldStorage(fp=request.body,environ=environ,keep_blank_values=1) 282 # The same detection used by FieldStorage to detect multipart POSTs 283 is_multipart = dpost.type[:10] == 'multipart/' 284 request.body.seek(0) 285 isle25 = sys.version_info[1] <= 5 286 287 def listify(a): 288 return (not isinstance(a,list) and [a]) or a
289 try: 290 keys = sorted(dpost) 291 except TypeError: 292 keys = [] 293 for key in keys: 294 dpk = dpost[key] 295 # if en element is not a file replace it with its value else leave it alone 296 if isinstance(dpk, list): 297 if not dpk[0].filename: 298 value = [x.value for x in dpk] 299 else: 300 value = [x for x in dpk] 301 elif not dpk.filename: 302 value = dpk.value 303 else: 304 value = dpk 305 pvalue = listify(value) 306 if key in request.vars: 307 gvalue = listify(request.vars[key]) 308 if isle25: 309 value = pvalue + gvalue 310 elif is_multipart: 311 pvalue = pvalue[len(gvalue):] 312 else: 313 pvalue = pvalue[:-len(gvalue)] 314 request.vars[key] = value 315 if len(pvalue): 316 request.post_vars[key] = (len(pvalue)>1 and pvalue) or pvalue[0] 317 318
319 -def wsgibase(environ, responder):
320 """ 321 this is the gluon wsgi application. the first function called when a page 322 is requested (static or dynamic). it can be called by paste.httpserver 323 or by apache mod_wsgi. 324 325 - fills request with info 326 - the environment variables, replacing '.' with '_' 327 - adds web2py path and version info 328 - compensates for fcgi missing path_info and query_string 329 - validates the path in url 330 331 The url path must be either: 332 333 1. for static pages: 334 335 - /<application>/static/<file> 336 337 2. for dynamic pages: 338 339 - /<application>[/<controller>[/<function>[/<sub>]]][.<extension>] 340 - (sub may go several levels deep, currently 3 levels are supported: 341 sub1/sub2/sub3) 342 343 The naming conventions are: 344 345 - application, controller, function and extension may only contain 346 [a-zA-Z0-9_] 347 - file and sub may also contain '-', '=', '.' and '/' 348 """ 349 350 current.__dict__.clear() 351 request = Request() 352 response = Response() 353 session = Session() 354 request.env.web2py_path = global_settings.applications_parent 355 request.env.web2py_version = web2py_version 356 request.env.update(global_settings) 357 static_file = False 358 try: 359 try: 360 try: 361 # ################################################## 362 # handle fcgi missing path_info and query_string 363 # select rewrite parameters 364 # rewrite incoming URL 365 # parse rewritten header variables 366 # parse rewritten URL 367 # serve file if static 368 # ################################################## 369 370 if not environ.get('PATH_INFO',None) and \ 371 environ.get('REQUEST_URI',None): 372 # for fcgi, get path_info and query_string from request_uri 373 items = environ['REQUEST_URI'].split('?') 374 environ['PATH_INFO'] = items[0] 375 if len(items) > 1: 376 environ['QUERY_STRING'] = items[1] 377 else: 378 environ['QUERY_STRING'] = '' 379 if not environ.get('HTTP_HOST',None): 380 environ['HTTP_HOST'] = '%s:%s' % (environ.get('SERVER_NAME'), 381 environ.get('SERVER_PORT')) 382 383 (static_file, environ) = rewrite.url_in(request, environ) 384 if static_file: 385 if request.env.get('query_string', '')[:10] == 'attachment': 386 response.headers['Content-Disposition'] = 'attachment' 387 response.stream(static_file, request=request) 388 389 # ################################################## 390 # fill in request items 391 # ################################################## 392 393 http_host = request.env.http_host.split(':',1)[0] 394 395 local_hosts = [http_host,'::1','127.0.0.1','::ffff:127.0.0.1'] 396 if not global_settings.web2py_runtime_gae: 397 local_hosts += [socket.gethostname(), 398 socket.gethostbyname(http_host)] 399 request.client = get_client(request.env) 400 request.folder = abspath('applications', 401 request.application) + os.sep 402 x_req_with = str(request.env.http_x_requested_with).lower() 403 request.ajax = x_req_with == 'xmlhttprequest' 404 request.cid = request.env.http_web2py_component_element 405 request.is_local = request.env.remote_addr in local_hosts 406 request.is_https = request.env.wsgi_url_scheme \ 407 in ['https', 'HTTPS'] or request.env.https == 'on' 408 409 # ################################################## 410 # compute a request.uuid to be used for tickets and toolbar 411 # ################################################## 412 413 response.uuid = request.compute_uuid() 414 415 # ################################################## 416 # access the requested application 417 # ################################################## 418 419 if not os.path.exists(request.folder): 420 if request.application == rewrite.thread.routes.default_application and request.application != 'welcome': 421 request.application = 'welcome' 422 redirect(Url(r=request)) 423 elif rewrite.thread.routes.error_handler: 424 redirect(Url(rewrite.thread.routes.error_handler['application'], 425 rewrite.thread.routes.error_handler['controller'], 426 rewrite.thread.routes.error_handler['function'], 427 args=request.application)) 428 else: 429 raise HTTP(404, 430 rewrite.thread.routes.error_message % 'invalid request', 431 web2py_error='invalid application') 432 request.url = Url(r=request, args=request.args, 433 extension=request.raw_extension) 434 435 # ################################################## 436 # build missing folders 437 # ################################################## 438 439 create_missing_app_folders(request) 440 441 # ################################################## 442 # get the GET and POST data 443 # ################################################## 444 445 parse_get_post_vars(request, environ) 446 447 # ################################################## 448 # expose wsgi hooks for convenience 449 # ################################################## 450 451 request.wsgi.environ = environ_aux(environ,request) 452 request.wsgi.start_response = lambda status='200', headers=[], \ 453 exec_info=None, response=response: \ 454 start_response_aux(status, headers, exec_info, response) 455 request.wsgi.middleware = lambda *a: middleware_aux(request,response,*a) 456 457 # ################################################## 458 # load cookies 459 # ################################################## 460 461 if request.env.http_cookie: 462 try: 463 request.cookies.load(request.env.http_cookie) 464 except Cookie.CookieError, e: 465 pass # invalid cookies 466 467 # ################################################## 468 # try load session or create new session file 469 # ################################################## 470 471 session.connect(request, response) 472 473 # ################################################## 474 # set no-cache headers 475 # ################################################## 476 477 response.headers['Content-Type'] = contenttype('.'+request.extension) 478 response.headers['Cache-Control'] = \ 479 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0' 480 response.headers['Expires'] = \ 481 time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime()) 482 response.headers['Pragma'] = 'no-cache' 483 484 # ################################################## 485 # run controller 486 # ################################################## 487 488 serve_controller(request, response, session) 489 490 except HTTP, http_response: 491 if static_file: 492 return http_response.to(responder) 493 494 if request.body: 495 request.body.close() 496 497 # ################################################## 498 # on success, try store session in database 499 # ################################################## 500 session._try_store_in_db(request, response) 501 502 # ################################################## 503 # on success, commit database 504 # ################################################## 505 506 if response._custom_commit: 507 response._custom_commit() 508 else: 509 BaseAdapter.close_all_instances('commit') 510 511 # ################################################## 512 # if session not in db try store session on filesystem 513 # this must be done after trying to commit database! 514 # ################################################## 515 516 session._try_store_on_disk(request, response) 517 518 # ################################################## 519 # store cookies in headers 520 # ################################################## 521 522 if request.cid: 523 524 if response.flash and not 'web2py-component-flash' in http_response.headers: 525 http_response.headers['web2py-component-flash'] = \ 526 dumps(str(response.flash).replace('\n','')) 527 if response.js and not 'web2py-component-command' in http_response.headers: 528 http_response.headers['web2py-component-command'] = \ 529 response.js.replace('\n','') 530 if session._forget: 531 del response.cookies[response.session_id_name] 532 elif session._secure: 533 response.cookies[response.session_id_name]['secure'] = True 534 if len(response.cookies)>0: 535 http_response.headers['Set-Cookie'] = \ 536 [str(cookie)[11:] for cookie in response.cookies.values()] 537 ticket=None 538 539 except RestrictedError, e: 540 541 if request.body: 542 request.body.close() 543 544 # ################################################## 545 # on application error, rollback database 546 # ################################################## 547 548 ticket = e.log(request) or 'unknown' 549 if response._custom_rollback: 550 response._custom_rollback() 551 else: 552 BaseAdapter.close_all_instances('rollback') 553 554 http_response = \ 555 HTTP(500, 556 rewrite.thread.routes.error_message_ticket % dict(ticket=ticket), 557 web2py_error='ticket %s' % ticket) 558 559 except: 560 561 if request.body: 562 request.body.close() 563 564 # ################################################## 565 # on application error, rollback database 566 # ################################################## 567 568 try: 569 if response._custom_rollback: 570 response._custom_rollback() 571 else: 572 BaseAdapter.close_all_instances('rollback') 573 except: 574 pass 575 e = RestrictedError('Framework', '', '', locals()) 576 ticket = e.log(request) or 'unrecoverable' 577 http_response = \ 578 HTTP(500, 579 rewrite.thread.routes.error_message_ticket % dict(ticket=ticket), 580 web2py_error='ticket %s' % ticket) 581 582 finally: 583 if response and hasattr(response, 'session_file') and response.session_file: 584 response.session_file.close() 585 # if global_settings.debugging: 586 # import gluon.debug 587 # gluon.debug.stop_trace() 588 589 session._unlock(response) 590 http_response = rewrite.try_redirect_on_error(http_response,request,ticket) 591 if global_settings.web2py_crontype == 'soft': 592 newcron.softcron(global_settings.applications_parent).start() 593 return http_response.to(responder)
594 595
596 -def save_password(password, port):
597 """ 598 used by main() to save the password in the parameters_port.py file. 599 """ 600 601 password_file = abspath('parameters_%i.py' % port) 602 if password == '<random>': 603 # make up a new password 604 chars = string.letters + string.digits 605 password = ''.join([random.choice(chars) for i in range(8)]) 606 cpassword = CRYPT()(password)[0] 607 print '******************* IMPORTANT!!! ************************' 608 print 'your admin password is "%s"' % password 609 print '*********************************************************' 610 elif password == '<recycle>': 611 # reuse the current password if any 612 if os.path.exists(password_file): 613 return 614 else: 615 password = '' 616 elif password.startswith('<pam_user:'): 617 # use the pam password for specified user 618 cpassword = password[1:-1] 619 else: 620 # use provided password 621 cpassword = CRYPT()(password)[0] 622 fp = open(password_file, 'w') 623 if password: 624 fp.write('password="%s"\n' % cpassword) 625 else: 626 fp.write('password=None\n') 627 fp.close()
628 629
630 -def appfactory(wsgiapp=wsgibase, 631 logfilename='httpserver.log', 632 profilerfilename='profiler.log'):
633 """ 634 generates a wsgi application that does logging and profiling and calls 635 wsgibase 636 637 .. function:: gluon.main.appfactory( 638 [wsgiapp=wsgibase 639 [, logfilename='httpserver.log' 640 [, profilerfilename='profiler.log']]]) 641 642 """ 643 if profilerfilename and os.path.exists(profilerfilename): 644 os.unlink(profilerfilename) 645 locker = thread.allocate_lock() 646 647 def app_with_logging(environ, responder): 648 """ 649 a wsgi app that does logging and profiling and calls wsgibase 650 """ 651 status_headers = [] 652 653 def responder2(s, h): 654 """ 655 wsgi responder app 656 """ 657 status_headers.append(s) 658 status_headers.append(h) 659 return responder(s, h)
660 661 time_in = time.time() 662 ret = [0] 663 if not profilerfilename: 664 ret[0] = wsgiapp(environ, responder2) 665 else: 666 import cProfile 667 import pstats 668 logger.warn('profiler is on. this makes web2py slower and serial') 669 670 locker.acquire() 671 cProfile.runctx('ret[0] = wsgiapp(environ, responder2)', 672 globals(), locals(), profilerfilename+'.tmp') 673 stat = pstats.Stats(profilerfilename+'.tmp') 674 stat.stream = cStringIO.StringIO() 675 stat.strip_dirs().sort_stats("time").print_stats(80) 676 profile_out = stat.stream.getvalue() 677 profile_file = open(profilerfilename, 'a') 678 profile_file.write('%s\n%s\n%s\n%s\n\n' % \ 679 ('='*60, environ['PATH_INFO'], '='*60, profile_out)) 680 profile_file.close() 681 locker.release() 682 try: 683 line = '%s, %s, %s, %s, %s, %s, %f\n' % ( 684 environ['REMOTE_ADDR'], 685 datetime.datetime.today().strftime('%Y-%m-%d %H:%M:%S'), 686 environ['REQUEST_METHOD'], 687 environ['PATH_INFO'].replace(',', '%2C'), 688 environ['SERVER_PROTOCOL'], 689 (status_headers[0])[:3], 690 time.time() - time_in, 691 ) 692 if not logfilename: 693 sys.stdout.write(line) 694 elif isinstance(logfilename, str): 695 write_file(logfilename, line, 'a') 696 else: 697 logfilename.write(line) 698 except: 699 pass 700 return ret[0] 701 702 return app_with_logging 703 704
705 -class HttpServer(object):
706 """ 707 the web2py web server (Rocket) 708 """ 709
710 - def __init__( 711 self, 712 ip='127.0.0.1', 713 port=8000, 714 password='', 715 pid_filename='httpserver.pid', 716 log_filename='httpserver.log', 717 profiler_filename=None, 718 ssl_certificate=None, 719 ssl_private_key=None, 720 min_threads=None, 721 max_threads=None, 722 server_name=None, 723 request_queue_size=5, 724 timeout=10, 725 shutdown_timeout=None, # Rocket does not use a shutdown timeout 726 path=None, 727 interfaces=None # Rocket is able to use several interfaces - must be list of socket-tuples as string 728 ):
729 """ 730 starts the web server. 731 """ 732 733 if interfaces: 734 # if interfaces is specified, it must be tested for rocket parameter correctness 735 # not necessarily completely tested (e.g. content of tuples or ip-format) 736 import types 737 if isinstance(interfaces,types.ListType): 738 for i in interfaces: 739 if not isinstance(i,types.TupleType): 740 raise "Wrong format for rocket interfaces parameter - see http://packages.python.org/rocket/" 741 else: 742 raise "Wrong format for rocket interfaces parameter - see http://packages.python.org/rocket/" 743 744 if path: 745 # if a path is specified change the global variables so that web2py 746 # runs from there instead of cwd or os.environ['web2py_path'] 747 global web2py_path 748 path = os.path.normpath(path) 749 web2py_path = path 750 global_settings.applications_parent = path 751 os.chdir(path) 752 [add_path_first(p) for p in (path, abspath('site-packages'), "")] 753 754 save_password(password, port) 755 self.pid_filename = pid_filename 756 if not server_name: 757 server_name = socket.gethostname() 758 logger.info('starting web server...') 759 rocket.SERVER_NAME = server_name 760 sock_list = [ip, port] 761 if not ssl_certificate or not ssl_private_key: 762 logger.info('SSL is off') 763 elif not rocket.ssl: 764 logger.warning('Python "ssl" module unavailable. SSL is OFF') 765 elif not os.path.exists(ssl_certificate): 766 logger.warning('unable to open SSL certificate. SSL is OFF') 767 elif not os.path.exists(ssl_private_key): 768 logger.warning('unable to open SSL private key. SSL is OFF') 769 else: 770 sock_list.extend([ssl_private_key, ssl_certificate]) 771 logger.info('SSL is ON') 772 app_info = {'wsgi_app': appfactory(wsgibase, 773 log_filename, 774 profiler_filename) } 775 776 self.server = rocket.Rocket(interfaces or tuple(sock_list), 777 method='wsgi', 778 app_info=app_info, 779 min_threads=min_threads, 780 max_threads=max_threads, 781 queue_size=int(request_queue_size), 782 timeout=int(timeout), 783 handle_signals=False, 784 )
785 786
787 - def start(self):
788 """ 789 start the web server 790 """ 791 try: 792 signal.signal(signal.SIGTERM, lambda a, b, s=self: s.stop()) 793 signal.signal(signal.SIGINT, lambda a, b, s=self: s.stop()) 794 except: 795 pass 796 write_file(self.pid_filename, str(os.getpid())) 797 self.server.start()
798
799 - def stop(self, stoplogging=False):
800 """ 801 stop cron and the web server 802 """ 803 newcron.stopcron() 804 self.server.stop(stoplogging) 805 try: 806 os.unlink(self.pid_filename) 807 except: 808 pass
809