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