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