def _error_response(traceback_string): """Create a flask response for an error.""" response = flask.Response(traceback_string, status=500, mimetype='text/plain') response.headers.add('Access-Control-Allow-Origin', '*') return response
def quit(): # Taken from http://flask.pocoo.org/snippets/67/ shutdown_func = flask.request.environ.get('werkzeug.server.shutdown') if shutdown_func is None: raise RuntimeError('Not running with the Werkzeug Server: %s' % flask.request.environ) shutdown_func() return flask.Response('Server shutting down...', mimetype='text/plain')
def ping(): """Simple handler used to check if the server is running. If 'genfiles' arg is specified, only respond with a 200 if our genfiles dir is the same as the specified one. See: kake.server_client.start_server """ if flask.request.args.get('genfiles'): if project_root.join('genfiles') != flask.request.args.get('genfiles'): flask.abort(400) return return flask.Response('pong', mimetype='text/plain')
def serve_sourcemap(filename): """The sourcemap is automatically created along with its file.""" # This forces the .map file to get made too, if filename has one. non_map_response = serve_genfile(filename) # We'll say the .map file was last modified when its corresponding # .js/.css file was. last_modified_time = non_map_response.headers['Last-modified'] abspath = project_root.join('genfiles', filename + '.map') try: with open(abspath) as f: content = f.read() except (IOError, OSError): flask.abort(404) # The consensus is that sourcemap files should have type json: # http://stackoverflow.com/questions/18809567/what-is-the-correct-mime-type-for-min-map-javascript-source-files response = flask.Response(content, mimetype='application/json') _add_caching_headers(response, last_modified_time) # TODO(jlfwong): We always return a 200 for sourcemap files, when really we # should be returning a 304 sometimes based on the If-Modified-Since # header, like we do for non-sourcemap files. return response
def outdir(): """Simple handler to report the directory where we write our files. See: kake.server_client.start_server """ return flask.Response(project_root.join('genfiles'), mimetype='text/plain')
def serve_genfile(filename): """Serve a file from genfiles/, building it first if necessary. Arguments: filename: the filename to serve, relative to ka-root/genfiles/ _force: force re-build of filename even if it's up-to-date (query parameters): all other parameters a=b, where a does not start with an underscore, are added to the kake context-dict (as {a: b}). Parameters starting with _ are *not* added to the context dict. """ abspath = project_root.join('genfiles', filename) # This converts a werkzeug MultiDict to a normal dict. context = dict((k, v) for (k, v) in flask.request.args.iteritems() if not k.startswith('_')) force = flask.request.args.get('_force', False) # The call to build below will modify the context, but we want # the original context to pass through to the SourceMap header. user_context = context.copy() # TODO(csilvers): use a file-watcher to remove files from # _LASTMOD_TIMES as they change. Then we don't need to call # kake.make.build() at all if filename is in _LASTMOD_TIMES. # To do this, we'd need to keep a map from filename to all # files that depend on it. We could also update filemod_db's # mtime-cache via this file-watcher. try: with _BUILD_LOCK: # We pass in None for the checkpoint interval to prevent automatic # syncing of the filemod db, since we do that ourselves. file_changed = kake.make.build(os.path.join('genfiles', filename), context, force=force, checkpoint_interval=None) maybe_sync_filemod_db() except compile_rule.GracefulCompileFailure as e: # If the page is requested directly, just let the regular werkzeug # debugger display the error, otherwise serve the graceful response. if flask.request.accept_mimetypes.best == 'text/html': raise mimetype = mimetypes.guess_type(filename)[0] response = flask.Response(e.graceful_response, mimetype=mimetype) response.headers.add('Access-Control-Allow-Origin', '*') return response except compile_rule.BadRequestFailure as failure: return flask.Response( "BAD REQUEST: %s\n" % failure.message, mimetype="text/*", status=400) except Exception: # If it's a normal http request, re-raise and get the cool # werkzeug debugger output. If it's a javascript (XHR) # request, give our own output which is less cool but safer # to use with Access-Control-Allow-Origin. if flask.request.headers.get('Origin'): return _error_response(traceback.format_exc()) raise if file_changed or filename not in _LASTMOD_TIMES: mtime = os.path.getmtime(abspath) dtime = datetime.datetime.fromtimestamp(mtime) _LASTMOD_TIMES[filename] = dtime.strftime("%a, %d %b %Y %H:%M:%S GMT") # @Nolint(API expected English date-names) # If the file hasn't changed, and the etag matches, return a 304. client_mtime = flask.request.headers.get("If-Modified-Since") if client_mtime == _LASTMOD_TIMES[filename]: response = flask.Response(status=304) _add_caching_headers(response, _LASTMOD_TIMES[filename]) _maybe_add_sourcemap_header(response, filename, user_context) return response with open(abspath) as f: content = f.read() response = flask.Response(content, mimetype=mimetypes.guess_type(filename)[0]) _add_caching_headers(response, _LASTMOD_TIMES[filename]) # If we have a sourcemap, tell the client. _maybe_add_sourcemap_header(response, filename, user_context) return response