選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 
 
 
 

232 行
6.5 KiB

  1. # -*- coding: utf-8 -*-
  2. # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
  3. # MIT License. See license.txt
  4. from __future__ import unicode_literals
  5. import os
  6. import MySQLdb
  7. from werkzeug.wrappers import Request
  8. from werkzeug.local import LocalManager
  9. from werkzeug.exceptions import HTTPException, NotFound
  10. from werkzeug.contrib.profiler import ProfilerMiddleware
  11. from werkzeug.wsgi import SharedDataMiddleware
  12. import frappe
  13. import frappe.handler
  14. import frappe.auth
  15. import frappe.api
  16. import frappe.async
  17. import frappe.utils.response
  18. import frappe.website.render
  19. from frappe.utils import get_site_name
  20. from frappe.middlewares import StaticDataMiddleware
  21. from frappe.utils.error import make_error_snapshot
  22. from frappe.core.doctype.communication.comment import update_comments_in_parent_after_request
  23. from frappe import _
  24. local_manager = LocalManager([frappe.local])
  25. _site = None
  26. _sites_path = os.environ.get("SITES_PATH", ".")
  27. class RequestContext(object):
  28. def __init__(self, environ):
  29. self.request = Request(environ)
  30. def __enter__(self):
  31. init_request(self.request)
  32. def __exit__(self, type, value, traceback):
  33. frappe.destroy()
  34. @Request.application
  35. def application(request):
  36. response = None
  37. try:
  38. rollback = True
  39. init_request(request)
  40. if frappe.local.form_dict.cmd:
  41. response = frappe.handler.handle()
  42. elif frappe.request.path.startswith("/api/"):
  43. if frappe.local.form_dict.data is None:
  44. frappe.local.form_dict.data = request.get_data()
  45. response = frappe.api.handle()
  46. elif frappe.request.path.startswith('/backups'):
  47. response = frappe.utils.response.download_backup(request.path)
  48. elif frappe.request.path.startswith('/private/files/'):
  49. response = frappe.utils.response.download_private_file(request.path)
  50. elif frappe.local.request.method in ('GET', 'HEAD', 'POST'):
  51. response = frappe.website.render.render()
  52. else:
  53. raise NotFound
  54. except HTTPException as e:
  55. return e
  56. except frappe.SessionStopped as e:
  57. response = frappe.utils.response.handle_session_stopped()
  58. except Exception as e:
  59. response = handle_exception(e)
  60. else:
  61. rollback = after_request(rollback)
  62. finally:
  63. if frappe.local.request.method in ("POST", "PUT") and frappe.db and rollback:
  64. frappe.db.rollback()
  65. # set cookies
  66. if response and hasattr(frappe.local, 'cookie_manager'):
  67. frappe.local.cookie_manager.flush_cookies(response=response)
  68. frappe.destroy()
  69. return response
  70. def init_request(request):
  71. frappe.local.request = request
  72. frappe.local.is_ajax = frappe.get_request_header("X-Requested-With")=="XMLHttpRequest"
  73. site = _site or request.headers.get('X-Frappe-Site-Name') or get_site_name(request.host)
  74. frappe.init(site=site, sites_path=_sites_path)
  75. if not (frappe.local.conf and frappe.local.conf.db_name):
  76. # site does not exist
  77. raise NotFound
  78. if frappe.local.conf.get('maintenance_mode'):
  79. raise frappe.SessionStopped
  80. make_form_dict(request)
  81. frappe.local.http_request = frappe.auth.HTTPRequest()
  82. def make_form_dict(request):
  83. frappe.local.form_dict = frappe._dict({ k:v[0] if isinstance(v, (list, tuple)) else v \
  84. for k, v in (request.form or request.args).iteritems() })
  85. if "_" in frappe.local.form_dict:
  86. # _ is passed by $.ajax so that the request is not cached by the browser. So, remove _ from form_dict
  87. frappe.local.form_dict.pop("_")
  88. def handle_exception(e):
  89. response = None
  90. http_status_code = getattr(e, "http_status_code", 500)
  91. return_as_message = False
  92. if frappe.local.is_ajax or 'application/json' in frappe.local.request.headers.get('Accept', ''):
  93. # handle ajax responses first
  94. # if the request is ajax, send back the trace or error message
  95. response = frappe.utils.response.report_error(http_status_code)
  96. elif (http_status_code==500
  97. and isinstance(e, MySQLdb.OperationalError)
  98. and e.args[0] in (1205, 1213)):
  99. # 1205 = lock wait timeout
  100. # 1213 = deadlock
  101. # code 409 represents conflict
  102. http_status_code = 508
  103. elif http_status_code==401:
  104. frappe.respond_as_web_page(_("Session Expired"),
  105. _("Your session has expired, please login again to continue."),
  106. http_status_code=http_status_code, indicator_color='red')
  107. return_as_message = True
  108. elif http_status_code==403:
  109. frappe.respond_as_web_page(_("Not Permitted"),
  110. _("You do not have enough permissions to complete the action"),
  111. http_status_code=http_status_code, indicator_color='red')
  112. return_as_message = True
  113. elif http_status_code==404:
  114. frappe.respond_as_web_page(_("Not Found"),
  115. _("The resource you are looking for is not available"),
  116. http_status_code=http_status_code, indicator_color='red')
  117. return_as_message = True
  118. else:
  119. traceback = "<pre>"+frappe.get_traceback()+"</pre>"
  120. if frappe.local.flags.disable_traceback:
  121. traceback = ""
  122. frappe.respond_as_web_page("Server Error",
  123. traceback, http_status_code=http_status_code,
  124. indicator_color='red')
  125. return_as_message = True
  126. if e.__class__ == frappe.AuthenticationError:
  127. if hasattr(frappe.local, "login_manager"):
  128. frappe.local.login_manager.clear_cookies()
  129. if http_status_code >= 500:
  130. frappe.logger().error('Request Error', exc_info=True)
  131. make_error_snapshot(e)
  132. if return_as_message:
  133. response = frappe.website.render.render("message", http_status_code=http_status_code)
  134. return response
  135. def after_request(rollback):
  136. if (frappe.local.request.method in ("POST", "PUT") or frappe.local.flags.commit) and frappe.db:
  137. if frappe.db.transaction_writes:
  138. frappe.db.commit()
  139. rollback = False
  140. # update session
  141. if getattr(frappe.local, "session_obj", None):
  142. updated_in_db = frappe.local.session_obj.update()
  143. if updated_in_db:
  144. frappe.db.commit()
  145. rollback = False
  146. update_comments_in_parent_after_request()
  147. return rollback
  148. application = local_manager.make_middleware(application)
  149. def serve(port=8000, profile=False, site=None, sites_path='.'):
  150. global application, _site, _sites_path
  151. _site = site
  152. _sites_path = sites_path
  153. from werkzeug.serving import run_simple
  154. if profile:
  155. application = ProfilerMiddleware(application, sort_by=('cumtime', 'calls'))
  156. if not os.environ.get('NO_STATICS'):
  157. application = SharedDataMiddleware(application, {
  158. b'/assets': os.path.join(sites_path, 'assets').encode("utf-8"),
  159. })
  160. application = StaticDataMiddleware(application, {
  161. b'/files': os.path.abspath(sites_path).encode("utf-8")
  162. })
  163. application.debug = True
  164. application.config = {
  165. 'SERVER_NAME': 'localhost:8000'
  166. }
  167. in_test_env = os.environ.get('CI')
  168. run_simple('0.0.0.0', int(port), application,
  169. use_reloader=not in_test_env,
  170. use_debugger=not in_test_env,
  171. use_evalex=not in_test_env,
  172. threaded=True)