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, write_file, parse_version 
 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   
 38  #  Remarks: 
 39  #  calling script has inserted path to script directory into sys.path 
 40  #  applications_parent (path to applications/, site-packages/ etc) 
 41  #  defaults to that directory set sys.path to 
 42  #  ("", gluon_parent/site-packages, gluon_parent, ...) 
 43  # 
 44  #  this is wrong: 
 45  #  web2py_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 
 46  #  because we do not want the path to this file which may be Library.zip 
 47  #  gluon_parent is the directory containing gluon, web2py.py, logging.conf 
 48  #  and the handlers. 
 49  #  applications_parent (web2py_path) is the directory containing applications/ 
 50  #  and routes.py 
 51  #  The two are identical unless web2py_path is changed via the web2py.py -f folder option 
 52  #  main.web2py_path is the same as applications_parent (for backward compatibility) 
 53   
 54  if not hasattr(os, 'mkdir'): 
 55      global_settings.db_sessions = True 
 56  if global_settings.db_sessions is not True: 
 57      global_settings.db_sessions = set() 
 58  global_settings.gluon_parent = os.environ.get('web2py_path', os.getcwd()) 
 59  global_settings.applications_parent = global_settings.gluon_parent 
 60  web2py_path = global_settings.applications_parent # backward compatibility 
 61  global_settings.app_folders = set() 
 62  global_settings.debugging = False 
 63   
 64  custom_import_install(web2py_path) 
 65   
 66  create_missing_folders() 
 67   
 68  # set up logging for subsequent imports 
 69  import logging 
 70  import logging.config 
 71  logpath = abspath("logging.conf") 
 72  if os.path.exists(logpath): 
 73      logging.config.fileConfig(abspath("logging.conf")) 
 74  else: 
 75      logging.basicConfig() 
 76  logger = logging.getLogger("web2py") 
 77   
 78  from restricted import RestrictedError 
 79  from http import HTTP, redirect 
 80  from globals import Request, Response, Session 
 81  from compileapp import build_environment, run_models_in, \ 
 82      run_controller_in, run_view_in 
 83  from fileutils import copystream 
 84  from contenttype import contenttype 
 85  from dal import BaseAdapter 
 86  from settings import global_settings 
 87  from validators import CRYPT 
 88  from cache import Cache 
 89  from html import URL as Url 
 90  import newcron 
 91  import rewrite 
 92   
 93  __all__ = ['wsgibase', 'save_password', 'appfactory', 'HttpServer'] 
 94   
 95  requests = 0    # gc timer 
 96   
 97  # Security Checks: validate URL and session_id here, 
 98  # accept_language is validated in languages 
 99   
100  # pattern used to validate client address 
101  regex_client = re.compile('[\w\-:]+(\.[\w\-]+)*\.?')  # ## to account for IPV6 
102   
103  version_info = open(abspath('VERSION', gluon=True), 'r') 
104  web2py_version = parse_version(version_info.read().strip()) 
105  version_info.close() 
106  global_settings.web2py_version = web2py_version 
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 environ.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 == \ 417 rewrite.thread.routes.default_application \ 418 and request.application != 'welcome': 419 request.application = 'welcome' 420 redirect(Url(r=request)) 421 elif rewrite.thread.routes.error_handler: 422 _handler = rewrite.thread.routes.error_handler 423 redirect(Url(_handler['application'], 424 _handler['controller'], 425 _handler['function'], 426 args=request.application)) 427 else: 428 raise HTTP(404, rewrite.thread.routes.error_message \ 429 % 'invalid request', 430 web2py_error='invalid application') 431 elif not request.is_local and \ 432 os.path.exists(os.path.join(request.folder,'DISABLED')): 433 raise HTTP(200, "<html><body><h1>Down for maintenance</h1></body></html>") 434 request.url = Url(r=request, args=request.args, 435 extension=request.raw_extension) 436 437 # ################################################## 438 # build missing folders 439 # ################################################## 440 441 create_missing_app_folders(request) 442 443 # ################################################## 444 # get the GET and POST data 445 # ################################################## 446 447 parse_get_post_vars(request, environ) 448 449 # ################################################## 450 # expose wsgi hooks for convenience 451 # ################################################## 452 453 request.wsgi.environ = environ_aux(environ,request) 454 request.wsgi.start_response = \ 455 lambda status='200', headers=[], \ 456 exec_info=None, response=response: \ 457 start_response_aux(status, headers, exec_info, response) 458 request.wsgi.middleware = \ 459 lambda *a: middleware_aux(request,response,*a) 460 461 # ################################################## 462 # load cookies 463 # ################################################## 464 465 if request.env.http_cookie: 466 try: 467 request.cookies.load(request.env.http_cookie) 468 except Cookie.CookieError, e: 469 pass # invalid cookies 470 471 # ################################################## 472 # try load session or create new session file 473 # ################################################## 474 475 session.connect(request, response) 476 477 # ################################################## 478 # set no-cache headers 479 # ################################################## 480 481 response.headers['Content-Type'] = \ 482 contenttype('.'+request.extension) 483 response.headers['Cache-Control'] = \ 484 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0' 485 response.headers['Expires'] = \ 486 time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime()) 487 response.headers['Pragma'] = 'no-cache' 488 489 # ################################################## 490 # run controller 491 # ################################################## 492 493 serve_controller(request, response, session) 494 495 except HTTP, http_response: 496 if static_file: 497 return http_response.to(responder) 498 499 if request.body: 500 request.body.close() 501 502 # ################################################## 503 # on success, try store session in database 504 # ################################################## 505 session._try_store_in_db(request, response) 506 507 # ################################################## 508 # on success, commit database 509 # ################################################## 510 511 if response.do_not_commit is True: 512 BaseAdapter.close_all_instances(None) 513 elif response._custom_commit: 514 response._custom_commit() 515 else: 516 BaseAdapter.close_all_instances('commit') 517 518 # ################################################## 519 # if session not in db try store session on filesystem 520 # this must be done after trying to commit database! 521 # ################################################## 522 523 session._try_store_on_disk(request, response) 524 525 # ################################################## 526 # store cookies in headers 527 # ################################################## 528 529 if request.cid: 530 531 if response.flash and not 'web2py-component-flash' in http_response.headers: 532 http_response.headers['web2py-component-flash'] = \ 533 str(response.flash).replace('\n','') 534 if response.js and not 'web2py-component-command' in http_response.headers: 535 http_response.headers['web2py-component-command'] = \ 536 response.js.replace('\n','') 537 if session._forget and \ 538 response.session_id_name in response.cookies: 539 del response.cookies[response.session_id_name] 540 elif session._secure: 541 response.cookies[response.session_id_name]['secure'] = True 542 if len(response.cookies)>0: 543 http_response.headers['Set-Cookie'] = \ 544 [str(cookie)[11:] for cookie in response.cookies.values()] 545 ticket=None 546 547 except RestrictedError, e: 548 549 if request.body: 550 request.body.close() 551 552 # ################################################## 553 # on application error, rollback database 554 # ################################################## 555 556 ticket = e.log(request) or 'unknown' 557 if response._custom_rollback: 558 response._custom_rollback() 559 else: 560 BaseAdapter.close_all_instances('rollback') 561 562 http_response = \ 563 HTTP(500, rewrite.thread.routes.error_message_ticket % \ 564 dict(ticket=ticket), 565 web2py_error='ticket %s' % ticket) 566 567 except: 568 569 if request.body: 570 request.body.close() 571 572 # ################################################## 573 # on application error, rollback database 574 # ################################################## 575 576 try: 577 if response._custom_rollback: 578 response._custom_rollback() 579 else: 580 BaseAdapter.close_all_instances('rollback') 581 except: 582 pass 583 e = RestrictedError('Framework', '', '', locals()) 584 ticket = e.log(request) or 'unrecoverable' 585 http_response = \ 586 HTTP(500, rewrite.thread.routes.error_message_ticket \ 587 % dict(ticket=ticket), 588 web2py_error='ticket %s' % ticket) 589 590 finally: 591 if response and hasattr(response, 'session_file') \ 592 and response.session_file: 593 response.session_file.close() 594 # if global_settings.debugging: 595 # import gluon.debug 596 # gluon.debug.stop_trace() 597 598 session._unlock(response) 599 http_response, new_environ = rewrite.try_rewrite_on_error( 600 http_response, request, environ, ticket) 601 if not http_response: 602 return wsgibase(new_environ,responder) 603 if global_settings.web2py_crontype == 'soft': 604 newcron.softcron(global_settings.applications_parent).start() 605 return http_response.to(responder)
606 607
608 -def save_password(password, port):
609 """ 610 used by main() to save the password in the parameters_port.py file. 611 """ 612 613 password_file = abspath('parameters_%i.py' % port) 614 if password == '<random>': 615 # make up a new password 616 chars = string.letters + string.digits 617 password = ''.join([random.choice(chars) for i in range(8)]) 618 cpassword = CRYPT()(password)[0] 619 print '******************* IMPORTANT!!! ************************' 620 print 'your admin password is "%s"' % password 621 print '*********************************************************' 622 elif password == '<recycle>': 623 # reuse the current password if any 624 if os.path.exists(password_file): 625 return 626 else: 627 password = '' 628 elif password.startswith('<pam_user:'): 629 # use the pam password for specified user 630 cpassword = password[1:-1] 631 else: 632 # use provided password 633 cpassword = CRYPT()(password)[0] 634 fp = open(password_file, 'w') 635 if password: 636 fp.write('password="%s"\n' % cpassword) 637 else: 638 fp.write('password=None\n') 639 fp.close()
640 641
642 -def appfactory(wsgiapp=wsgibase, 643 logfilename='httpserver.log', 644 profilerfilename='profiler.log'):
645 """ 646 generates a wsgi application that does logging and profiling and calls 647 wsgibase 648 649 .. function:: gluon.main.appfactory( 650 [wsgiapp=wsgibase 651 [, logfilename='httpserver.log' 652 [, profilerfilename='profiler.log']]]) 653 654 """ 655 if profilerfilename and os.path.exists(profilerfilename): 656 os.unlink(profilerfilename) 657 locker = thread.allocate_lock() 658 659 def app_with_logging(environ, responder): 660 """ 661 a wsgi app that does logging and profiling and calls wsgibase 662 """ 663 status_headers = [] 664 665 def responder2(s, h): 666 """ 667 wsgi responder app 668 """ 669 status_headers.append(s) 670 status_headers.append(h) 671 return responder(s, h)
672 673 time_in = time.time() 674 ret = [0] 675 if not profilerfilename: 676 ret[0] = wsgiapp(environ, responder2) 677 else: 678 import cProfile 679 import pstats 680 logger.warn('profiler is on. this makes web2py slower and serial') 681 682 locker.acquire() 683 cProfile.runctx('ret[0] = wsgiapp(environ, responder2)', 684 globals(), locals(), profilerfilename+'.tmp') 685 stat = pstats.Stats(profilerfilename+'.tmp') 686 stat.stream = cStringIO.StringIO() 687 stat.strip_dirs().sort_stats("time").print_stats(80) 688 profile_out = stat.stream.getvalue() 689 profile_file = open(profilerfilename, 'a') 690 profile_file.write('%s\n%s\n%s\n%s\n\n' % \ 691 ('='*60, environ['PATH_INFO'], '='*60, profile_out)) 692 profile_file.close() 693 locker.release() 694 try: 695 line = '%s, %s, %s, %s, %s, %s, %f\n' % ( 696 environ['REMOTE_ADDR'], 697 datetime.datetime.today().strftime('%Y-%m-%d %H:%M:%S'), 698 environ['REQUEST_METHOD'], 699 environ['PATH_INFO'].replace(',', '%2C'), 700 environ['SERVER_PROTOCOL'], 701 (status_headers[0])[:3], 702 time.time() - time_in, 703 ) 704 if not logfilename: 705 sys.stdout.write(line) 706 elif isinstance(logfilename, str): 707 write_file(logfilename, line, 'a') 708 else: 709 logfilename.write(line) 710 except: 711 pass 712 return ret[0] 713 714 return app_with_logging 715 716
717 -class HttpServer(object):
718 """ 719 the web2py web server (Rocket) 720 """ 721
722 - def __init__( 723 self, 724 ip='127.0.0.1', 725 port=8000, 726 password='', 727 pid_filename='httpserver.pid', 728 log_filename='httpserver.log', 729 profiler_filename=None, 730 ssl_certificate=None, 731 ssl_private_key=None, 732 ssl_ca_certificate=None, 733 min_threads=None, 734 max_threads=None, 735 server_name=None, 736 request_queue_size=5, 737 timeout=10, 738 socket_timeout = 1, 739 shutdown_timeout=None, # Rocket does not use a shutdown timeout 740 path=None, 741 interfaces=None # Rocket is able to use several interfaces - must be list of socket-tuples as string 742 ):
743 """ 744 starts the web server. 745 """ 746 747 if interfaces: 748 # if interfaces is specified, it must be tested for rocket parameter correctness 749 # not necessarily completely tested (e.g. content of tuples or ip-format) 750 import types 751 if isinstance(interfaces,types.ListType): 752 for i in interfaces: 753 if not isinstance(i,types.TupleType): 754 raise "Wrong format for rocket interfaces parameter - see http://packages.python.org/rocket/" 755 else: 756 raise "Wrong format for rocket interfaces parameter - see http://packages.python.org/rocket/" 757 758 if path: 759 # if a path is specified change the global variables so that web2py 760 # runs from there instead of cwd or os.environ['web2py_path'] 761 global web2py_path 762 path = os.path.normpath(path) 763 web2py_path = path 764 global_settings.applications_parent = path 765 os.chdir(path) 766 [add_path_first(p) for p in (path, abspath('site-packages'), "")] 767 custom_import_install(web2py_path) 768 if os.path.exists("logging.conf"): 769 logging.config.fileConfig("logging.conf") 770 771 save_password(password, port) 772 self.pid_filename = pid_filename 773 if not server_name: 774 server_name = socket.gethostname() 775 logger.info('starting web server...') 776 rocket.SERVER_NAME = server_name 777 rocket.SOCKET_TIMEOUT = socket_timeout 778 sock_list = [ip, port] 779 if not ssl_certificate or not ssl_private_key: 780 logger.info('SSL is off') 781 elif not rocket.ssl: 782 logger.warning('Python "ssl" module unavailable. SSL is OFF') 783 elif not os.path.exists(ssl_certificate): 784 logger.warning('unable to open SSL certificate. SSL is OFF') 785 elif not os.path.exists(ssl_private_key): 786 logger.warning('unable to open SSL private key. SSL is OFF') 787 else: 788 sock_list.extend([ssl_private_key, ssl_certificate]) 789 if ssl_ca_certificate: 790 sock_list.append(ssl_ca_certificate) 791 792 logger.info('SSL is ON') 793 app_info = {'wsgi_app': appfactory(wsgibase, 794 log_filename, 795 profiler_filename) } 796 797 self.server = rocket.Rocket(interfaces or tuple(sock_list), 798 method='wsgi', 799 app_info=app_info, 800 min_threads=min_threads, 801 max_threads=max_threads, 802 queue_size=int(request_queue_size), 803 timeout=int(timeout), 804 handle_signals=False, 805 )
806 807
808 - def start(self):
809 """ 810 start the web server 811 """ 812 try: 813 signal.signal(signal.SIGTERM, lambda a, b, s=self: s.stop()) 814 signal.signal(signal.SIGINT, lambda a, b, s=self: s.stop()) 815 except: 816 pass 817 write_file(self.pid_filename, str(os.getpid())) 818 self.server.start()
819
820 - def stop(self, stoplogging=False):
821 """ 822 stop cron and the web server 823 """ 824 newcron.stopcron() 825 self.server.stop(stoplogging) 826 try: 827 os.unlink(self.pid_filename) 828 except: 829 pass
830