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