3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
19 from OpenSSL import SSL
20 from flask import Flask, request
21 from flask_restful import Api
22 from werkzeug.exceptions import InternalServerError
23 from yarf.handlers.pluginhandler import PluginLoader
24 from yarf.iniloader import ConfigError
25 import yarf.restfulargs as restfulconfig
26 import yarf.restfullogger as restlog
27 from yarf.helpers import remove_secrets
29 CRIT_RESP_LEN = 150000
35 def handle_excp(failure):
36 if isinstance(failure, socket.error):
37 app.logger.warning("Socket error, ignoring")
40 app.logger.error("Internal error: %s ", failure)
42 app.logger.info("Failure not defined... Ignoring.")
44 raise InternalServerError()
46 def get_config(args, logger):
48 config = restfulconfig.RestConfig()
50 config.parse(sys.argv[1:])
53 except ConfigError as error:
54 logger.error("Failed to start %s" % error)
59 app.logger.info('Request: remote_addr: %s method: %s endpoint: %s, user: %s', request.remote_addr,
60 request.method, remove_secrets(request.full_path), get_username())
62 def response_logger(response):
63 app.logger.info('Response: status: %s (Associated Request: remote_addr: %s, method: %s, endpoint: %s, user: %s)',
64 response.status, request.remote_addr, request.method,
65 remove_secrets(request.full_path), get_username())
67 if len(response.data) > CRIT_RESP_LEN:
68 app.logger.debug('Response\'s data is too big, truncating!')
69 app.logger.debug('Response\'s truncated data: %s', response.data[:CRIT_RESP_LEN])
71 app.logger.debug('Response\'s data: %s', response.data)
73 response.headers["Server"] = "Restapi"
79 return auth_method.get_authentication(request)[1]
80 except Exception as err: # pylint: disable=broad-except
81 app.logger.warn("Failed to get username from request returning empty. Err: %s", str(err))
85 def initialize(config, logger):
86 logger.info("Initializing...")
87 loglevel = logging.INFO if not config.get_debug() else logging.DEBUG
88 app.logger.setLevel(loglevel)
89 app.register_error_handler(Exception, handle_excp)
90 app.before_request(request_logger)
91 app.after_request(response_logger)
92 logger.error("%s", config.get_handler_dir())
93 p = PluginLoader(config.get_handler_dir(), api, config.get_auth_method())
94 auth_handler = p.get_auth_method()
95 handlers = p.get_modules()
96 for handler in handlers:
97 p.init_handler(handler)
99 for handler in restlog.get_log_handlers():
100 app.logger.addHandler(handler)
101 p.create_api_versionhandlers(handlers)
102 logger.info("Starting up...")
105 def get_wsgi_application():
106 logger = restlog.get_logger()
107 config = get_config(None, logger)
108 initialize(config, logger)
112 logger = restlog.get_logger()
113 config = get_config(sys.argv[1:], logger)
115 raise ConfigError("Failed to read config file")
116 initialize(config, logger)
118 run_params["debug"] = config.get_debug()
119 run_params["port"] = config.get_port()
120 run_params["host"] = config.get_ip()
121 # When this https://github.com/pallets/werkzeug/issues/954 is fixed then the error handling
122 # can be done in the error handler of app level
123 passthrough_errors = config.get_passthrough_errors()
124 run_params["passthrough_errors"] = passthrough_errors
125 run_params["threaded"] = config.is_threaded()
126 logger.debug("%s %s %s", run_params["debug"], run_params["port"], run_params["threaded"])
128 context = SSL.Context(SSL.SSLv23_METHOD)
129 context.use_privatekey_file(config.get_private_key())
130 context.use_certificate_file(config.get_certificate())
131 run_params['ssl_context'] = context
134 app.run(**run_params)
135 except Exception as err: # pylint: disable=broad-except
136 logger.warning("Caught exception but starting again %s", err)
137 if passthrough_errors:
141 logger.warning("Die in piece %s", err)
142 func = request.environ.get('werkzeug.server.shutdown')
144 raise RuntimeError('Not running with the Werkzeug Server')
149 if __name__ == '__main__':
152 except Exception as error:# pylint: disable=broad-except
153 print "Failure: %s" % error