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 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 and \ 528 response.session_id_name in response.cookies: 529 del response.cookies[response.session_id_name] 530 elif session._secure: 531 response.cookies[response.session_id_name]['secure'] = True 532 if len(response.cookies)>0: 533 http_response.headers['Set-Cookie'] = \ 534 [str(cookie)[11:] for cookie in response.cookies.values()] 535 ticket=None 536 537 except RestrictedError, e: 538 539 if request.body: 540 request.body.close() 541 542 # ################################################## 543 # on application error, rollback database 544 # ################################################## 545 546 ticket = e.log(request) or 'unknown' 547 if response._custom_rollback: 548 response._custom_rollback() 549 else: 550 BaseAdapter.close_all_instances('rollback') 551 552 http_response = \ 553 HTTP(500, 554 rewrite.thread.routes.error_message_ticket % dict(ticket=ticket), 555 web2py_error='ticket %s' % ticket) 556 557 except: 558 559 if request.body: 560 request.body.close() 561 562 # ################################################## 563 # on application error, rollback database 564 # ################################################## 565 566 try: 567 if response._custom_rollback: 568 response._custom_rollback() 569 else: 570 BaseAdapter.close_all_instances('rollback') 571 except: 572 pass 573 e = RestrictedError('Framework', '', '', locals()) 574 ticket = e.log(request) or 'unrecoverable' 575 http_response = \ 576 HTTP(500, 577 rewrite.thread.routes.error_message_ticket % dict(ticket=ticket), 578 web2py_error='ticket %s' % ticket) 579 580 finally: 581 if response and hasattr(response, 'session_file') and response.session_file: 582 response.session_file.close() 583 # if global_settings.debugging: 584 # import gluon.debug 585 # gluon.debug.stop_trace() 586 587 session._unlock(response) 588 http_response, new_environ = rewrite.try_rewrite_on_error( 589 http_response, request, environ, ticket) 590 if not http_response: 591 return wsgibase(new_environ,responder) 592 if global_settings.web2py_crontype == 'soft': 593 newcron.softcron(global_settings.applications_parent).start() 594 return http_response.to(responder)
595 596
597 -def save_password(password, port):
598 """ 599 used by main() to save the password in the parameters_port.py file. 600 """ 601 602 password_file = abspath('parameters_%i.py' % port) 603 if password == '<random>': 604 # make up a new password 605 chars = string.letters + string.digits 606 password = ''.join([random.choice(chars) for i in range(8)]) 607 cpassword = CRYPT()(password)[0] 608 print '******************* IMPORTANT!!! ************************' 609 print 'your admin password is "%s"' % password 610 print '*********************************************************' 611 elif password == '<recycle>': 612 # reuse the current password if any 613 if os.path.exists(password_file): 614 return 615 else: 616 password = '' 617 elif password.startswith('<pam_user:'): 618 # use the pam password for specified user 619 cpassword = password[1:-1] 620 else: 621 # use provided password 622 cpassword = CRYPT()(password)[0] 623 fp = open(password_file, 'w') 624 if password: 625 fp.write('password="%s"\n' % cpassword) 626 else: 627 fp.write('password=None\n') 628 fp.close()
629 630
631 -def appfactory(wsgiapp=wsgibase, 632 logfilename='httpserver.log', 633 profilerfilename='profiler.log'):
634 """ 635 generates a wsgi application that does logging and profiling and calls 636 wsgibase 637 638 .. function:: gluon.main.appfactory( 639 [wsgiapp=wsgibase 640 [, logfilename='httpserver.log' 641 [, profilerfilename='profiler.log']]]) 642 643 """ 644 if profilerfilename and os.path.exists(profilerfilename): 645 os.unlink(profilerfilename) 646 locker = thread.allocate_lock() 647 648 def app_with_logging(environ, responder): 649 """ 650 a wsgi app that does logging and profiling and calls wsgibase 651 """ 652 status_headers = [] 653 654 def responder2(s, h): 655 """ 656 wsgi responder app 657 """ 658 status_headers.append(s) 659 status_headers.append(h) 660 return responder(s, h)
661 662 time_in = time.time() 663 ret = [0] 664 if not profilerfilename: 665 ret[0] = wsgiapp(environ, responder2) 666 else: 667 import cProfile 668 import pstats 669 logger.warn('profiler is on. this makes web2py slower and serial') 670 671 locker.acquire() 672 cProfile.runctx('ret[0] = wsgiapp(environ, responder2)', 673 globals(), locals(), profilerfilename+'.tmp') 674 stat = pstats.Stats(profilerfilename+'.tmp') 675 stat.stream = cStringIO.StringIO() 676 stat.strip_dirs().sort_stats("time").print_stats(80) 677 profile_out = stat.stream.getvalue() 678 profile_file = open(profilerfilename, 'a') 679 profile_file.write('%s\n%s\n%s\n%s\n\n' % \ 680 ('='*60, environ['PATH_INFO'], '='*60, profile_out)) 681 profile_file.close() 682 locker.release() 683 try: 684 line = '%s, %s, %s, %s, %s, %s, %f\n' % ( 685 environ['REMOTE_ADDR'], 686 datetime.datetime.today().strftime('%Y-%m-%d %H:%M:%S'), 687 environ['REQUEST_METHOD'], 688 environ['PATH_INFO'].replace(',', '%2C'), 689 environ['SERVER_PROTOCOL'], 690 (status_headers[0])[:3], 691 time.time() - time_in, 692 ) 693 if not logfilename: 694 sys.stdout.write(line) 695 elif isinstance(logfilename, str): 696 write_file(logfilename, line, 'a') 697 else: 698 logfilename.write(line) 699 except: 700 pass 701 return ret[0] 702 703 return app_with_logging 704 705
706 -class HttpServer(object):
707 """ 708 the web2py web server (Rocket) 709 """ 710
711 - def __init__( 712 self, 713 ip='127.0.0.1', 714 port=8000, 715 password='', 716 pid_filename='httpserver.pid', 717 log_filename='httpserver.log', 718 profiler_filename=None, 719 ssl_certificate=None, 720 ssl_private_key=None, 721 min_threads=None, 722 max_threads=None, 723 server_name=None, 724 request_queue_size=5, 725 timeout=10, 726 shutdown_timeout=None, # Rocket does not use a shutdown timeout 727 path=None, 728 interfaces=None # Rocket is able to use several interfaces - must be list of socket-tuples as string 729 ):
730 """ 731 starts the web server. 732 """ 733 734 if interfaces: 735 # if interfaces is specified, it must be tested for rocket parameter correctness 736 # not necessarily completely tested (e.g. content of tuples or ip-format) 737 import types 738 if isinstance(interfaces,types.ListType): 739 for i in interfaces: 740 if not isinstance(i,types.TupleType): 741 raise "Wrong format for rocket interfaces parameter - see http://packages.python.org/rocket/" 742 else: 743 raise "Wrong format for rocket interfaces parameter - see http://packages.python.org/rocket/" 744 745 if path: 746 # if a path is specified change the global variables so that web2py 747 # runs from there instead of cwd or os.environ['web2py_path'] 748 global web2py_path 749 path = os.path.normpath(path) 750 web2py_path = path 751 global_settings.applications_parent = path 752 os.chdir(path) 753 [add_path_first(p) for p in (path, abspath('site-packages'), "")] 754 755 save_password(password, port) 756 self.pid_filename = pid_filename 757 if not server_name: 758 server_name = socket.gethostname() 759 logger.info('starting web server...') 760 rocket.SERVER_NAME = server_name 761 sock_list = [ip, port] 762 if not ssl_certificate or not ssl_private_key: 763 logger.info('SSL is off') 764 elif not rocket.ssl: 765 logger.warning('Python "ssl" module unavailable. SSL is OFF') 766 elif not os.path.exists(ssl_certificate): 767 logger.warning('unable to open SSL certificate. SSL is OFF') 768 elif not os.path.exists(ssl_private_key): 769 logger.warning('unable to open SSL private key. SSL is OFF') 770 else: 771 sock_list.extend([ssl_private_key, ssl_certificate]) 772 logger.info('SSL is ON') 773 app_info = {'wsgi_app': appfactory(wsgibase, 774 log_filename, 775 profiler_filename) } 776 777 self.server = rocket.Rocket(interfaces or tuple(sock_list), 778 method='wsgi', 779 app_info=app_info, 780 min_threads=min_threads, 781 max_threads=max_threads, 782 queue_size=int(request_queue_size), 783 timeout=int(timeout), 784 handle_signals=False, 785 )
786 787
788 - def start(self):
789 """ 790 start the web server 791 """ 792 try: 793 signal.signal(signal.SIGTERM, lambda a, b, s=self: s.stop()) 794 signal.signal(signal.SIGINT, lambda a, b, s=self: s.stop()) 795 except: 796 pass 797 write_file(self.pid_filename, str(os.getpid())) 798 self.server.start()
799
800 - def stop(self, stoplogging=False):
801 """ 802 stop cron and the web server 803 """ 804 newcron.stopcron() 805 self.server.stop(stoplogging) 806 try: 807 os.unlink(self.pid_filename) 808 except: 809 pass
810