Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 
 
 

318 wiersze
9.0 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. from six import iteritems
  7. import logging
  8. from werkzeug.local import LocalManager
  9. from werkzeug.wrappers import Request, Response
  10. from werkzeug.exceptions import HTTPException, NotFound
  11. from werkzeug.middleware.profiler import ProfilerMiddleware
  12. from werkzeug.middleware.shared_data import SharedDataMiddleware
  13. import frappe
  14. import frappe.handler
  15. import frappe.auth
  16. import frappe.api
  17. import frappe.utils.response
  18. import frappe.website.render
  19. from frappe.utils import get_site_name, sanitize_html
  20. from frappe.middlewares import StaticDataMiddleware
  21. from frappe.utils.error import make_error_snapshot
  22. from frappe.core.doctype.comment.comment import update_comments_in_parent_after_request
  23. from frappe import _
  24. import frappe.recorder
  25. import frappe.monitor
  26. import frappe.rate_limiter
  27. local_manager = LocalManager([frappe.local])
  28. _site = None
  29. _sites_path = os.environ.get("SITES_PATH", ".")
  30. class RequestContext(object):
  31. def __init__(self, environ):
  32. self.request = Request(environ)
  33. def __enter__(self):
  34. init_request(self.request)
  35. def __exit__(self, type, value, traceback):
  36. frappe.destroy()
  37. @Request.application
  38. def application(request):
  39. response = None
  40. try:
  41. rollback = True
  42. init_request(request)
  43. frappe.recorder.record()
  44. frappe.monitor.start()
  45. frappe.rate_limiter.apply()
  46. if request.method == "OPTIONS":
  47. response = Response()
  48. elif frappe.form_dict.cmd:
  49. response = frappe.handler.handle()
  50. elif request.path.startswith("/api/"):
  51. response = frappe.api.handle()
  52. elif request.path.startswith('/backups'):
  53. response = frappe.utils.response.download_backup(request.path)
  54. elif request.path.startswith('/private/files/'):
  55. response = frappe.utils.response.download_private_file(request.path)
  56. elif request.method in ('GET', 'HEAD', 'POST'):
  57. response = frappe.website.render.render()
  58. else:
  59. raise NotFound
  60. except HTTPException as e:
  61. return e
  62. except frappe.SessionStopped as e:
  63. response = frappe.utils.response.handle_session_stopped()
  64. except Exception as e:
  65. response = handle_exception(e)
  66. else:
  67. rollback = after_request(rollback)
  68. finally:
  69. if request.method in ("POST", "PUT") and frappe.db and rollback:
  70. frappe.db.rollback()
  71. frappe.rate_limiter.update()
  72. frappe.monitor.stop(response)
  73. frappe.recorder.dump()
  74. if hasattr(frappe.local, 'conf') and frappe.local.conf.enable_frappe_logger:
  75. frappe.logger("frappe.web", allow_site=frappe.local.site).info({
  76. "site": get_site_name(request.host),
  77. "remote_addr": getattr(request, "remote_addr", "NOTFOUND"),
  78. "base_url": getattr(request, "base_url", "NOTFOUND"),
  79. "full_path": getattr(request, "full_path", "NOTFOUND"),
  80. "method": getattr(request, "method", "NOTFOUND"),
  81. "scheme": getattr(request, "scheme", "NOTFOUND"),
  82. "http_status_code": getattr(response, "status_code", "NOTFOUND")
  83. })
  84. process_response(response)
  85. frappe.destroy()
  86. return response
  87. def init_request(request):
  88. frappe.local.request = request
  89. frappe.local.is_ajax = frappe.get_request_header("X-Requested-With")=="XMLHttpRequest"
  90. site = _site or request.headers.get('X-Frappe-Site-Name') or get_site_name(request.host)
  91. frappe.init(site=site, sites_path=_sites_path)
  92. if not (frappe.local.conf and frappe.local.conf.db_name):
  93. # site does not exist
  94. raise NotFound
  95. if frappe.local.conf.get('maintenance_mode'):
  96. frappe.connect()
  97. raise frappe.SessionStopped('Session Stopped')
  98. else:
  99. frappe.connect(set_admin_as_user=False)
  100. make_form_dict(request)
  101. if request.method != "OPTIONS":
  102. frappe.local.http_request = frappe.auth.HTTPRequest()
  103. def process_response(response):
  104. if not response:
  105. return
  106. # set cookies
  107. if hasattr(frappe.local, 'cookie_manager'):
  108. frappe.local.cookie_manager.flush_cookies(response=response)
  109. # rate limiter headers
  110. if hasattr(frappe.local, 'rate_limiter'):
  111. response.headers.extend(frappe.local.rate_limiter.headers())
  112. # CORS headers
  113. if hasattr(frappe.local, 'conf') and frappe.conf.allow_cors:
  114. set_cors_headers(response)
  115. def set_cors_headers(response):
  116. origin = frappe.request.headers.get('Origin')
  117. allow_cors = frappe.conf.allow_cors
  118. if not (origin and allow_cors):
  119. return
  120. if allow_cors != "*":
  121. if not isinstance(allow_cors, list):
  122. allow_cors = [allow_cors]
  123. if origin not in allow_cors:
  124. return
  125. response.headers.extend({
  126. 'Access-Control-Allow-Origin': origin,
  127. 'Access-Control-Allow-Credentials': 'true',
  128. 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
  129. 'Access-Control-Allow-Headers': ('Authorization,DNT,X-Mx-ReqToken,'
  130. 'Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,'
  131. 'Cache-Control,Content-Type')
  132. })
  133. def make_form_dict(request):
  134. import json
  135. request_data = request.get_data(as_text=True)
  136. if 'application/json' in (request.content_type or '') and request_data:
  137. args = json.loads(request_data)
  138. else:
  139. args = request.form or request.args
  140. if not isinstance(args, dict):
  141. frappe.throw("Invalid request arguments")
  142. try:
  143. frappe.local.form_dict = frappe._dict({ k:v[0] if isinstance(v, (list, tuple)) else v \
  144. for k, v in iteritems(args) })
  145. except IndexError:
  146. frappe.local.form_dict = frappe._dict(args)
  147. if "_" in frappe.local.form_dict:
  148. # _ is passed by $.ajax so that the request is not cached by the browser. So, remove _ from form_dict
  149. frappe.local.form_dict.pop("_")
  150. def handle_exception(e):
  151. response = None
  152. http_status_code = getattr(e, "http_status_code", 500)
  153. return_as_message = False
  154. if frappe.conf.get('developer_mode'):
  155. # don't fail silently
  156. print(frappe.get_traceback())
  157. if frappe.get_request_header('Accept') and (frappe.local.is_ajax or 'application/json' in frappe.get_request_header('Accept')):
  158. # handle ajax responses first
  159. # if the request is ajax, send back the trace or error message
  160. response = frappe.utils.response.report_error(http_status_code)
  161. elif (http_status_code==500
  162. and (frappe.db and isinstance(e, frappe.db.InternalError))
  163. and (frappe.db and (frappe.db.is_deadlocked(e) or frappe.db.is_timedout(e)))):
  164. http_status_code = 508
  165. elif http_status_code==401:
  166. frappe.respond_as_web_page(_("Session Expired"),
  167. _("Your session has expired, please login again to continue."),
  168. http_status_code=http_status_code, indicator_color='red')
  169. return_as_message = True
  170. elif http_status_code==403:
  171. frappe.respond_as_web_page(_("Not Permitted"),
  172. _("You do not have enough permissions to complete the action"),
  173. http_status_code=http_status_code, indicator_color='red')
  174. return_as_message = True
  175. elif http_status_code==404:
  176. frappe.respond_as_web_page(_("Not Found"),
  177. _("The resource you are looking for is not available"),
  178. http_status_code=http_status_code, indicator_color='red')
  179. return_as_message = True
  180. elif http_status_code == 429:
  181. response = frappe.rate_limiter.respond()
  182. else:
  183. traceback = "<pre>" + sanitize_html(frappe.get_traceback()) + "</pre>"
  184. # disable traceback in production if flag is set
  185. if frappe.local.flags.disable_traceback and not frappe.local.dev_server:
  186. traceback = ""
  187. frappe.respond_as_web_page("Server Error",
  188. traceback, http_status_code=http_status_code,
  189. indicator_color='red', width=640)
  190. return_as_message = True
  191. if e.__class__ == frappe.AuthenticationError:
  192. if hasattr(frappe.local, "login_manager"):
  193. frappe.local.login_manager.clear_cookies()
  194. if http_status_code >= 500:
  195. make_error_snapshot(e)
  196. if return_as_message:
  197. response = frappe.website.render.render("message",
  198. http_status_code=http_status_code)
  199. return response
  200. def after_request(rollback):
  201. if (frappe.local.request.method in ("POST", "PUT") or frappe.local.flags.commit) and frappe.db:
  202. if frappe.db.transaction_writes:
  203. frappe.db.commit()
  204. rollback = False
  205. # update session
  206. if getattr(frappe.local, "session_obj", None):
  207. updated_in_db = frappe.local.session_obj.update()
  208. if updated_in_db:
  209. frappe.db.commit()
  210. rollback = False
  211. update_comments_in_parent_after_request()
  212. return rollback
  213. application = local_manager.make_middleware(application)
  214. def serve(port=8000, profile=False, no_reload=False, no_threading=False, site=None, sites_path='.'):
  215. global application, _site, _sites_path
  216. _site = site
  217. _sites_path = sites_path
  218. from werkzeug.serving import run_simple
  219. if profile:
  220. application = ProfilerMiddleware(application, sort_by=('cumtime', 'calls'))
  221. if not os.environ.get('NO_STATICS'):
  222. application = SharedDataMiddleware(application, {
  223. str('/assets'): str(os.path.join(sites_path, 'assets'))
  224. })
  225. application = StaticDataMiddleware(application, {
  226. str('/files'): str(os.path.abspath(sites_path))
  227. })
  228. application.debug = True
  229. application.config = {
  230. 'SERVER_NAME': 'localhost:8000'
  231. }
  232. log = logging.getLogger('werkzeug')
  233. log.propagate = False
  234. in_test_env = os.environ.get('CI')
  235. if in_test_env:
  236. log.setLevel(logging.ERROR)
  237. run_simple('0.0.0.0', int(port), application,
  238. use_reloader=False if in_test_env else not no_reload,
  239. use_debugger=not in_test_env,
  240. use_evalex=not in_test_env,
  241. threaded=not no_threading)