1#!/usr/bin/env python3 2# Copyright (C) 2021 The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16import flask 17import os 18 19from google.cloud import storage 20 21BUCKET_NAME = 'perfetto.dev' 22 23app = flask.Flask(__name__) 24bucket = None 25is_local_testing_instance = False 26 27 28@app.route('/_ah/<path:path>') 29def ignore_app_engine_lifecycle(path): 30 return flask.abort(404) 31 32 33@app.route('/docs') 34def docs_redirect(): 35 return flask.redirect('/docs/', code=301) 36 37 38# Serve the requests from the GCS bucket. 39@app.route('/', methods=['GET']) 40@app.route('/<path:path>', methods=['GET']) 41def main(path=''): 42 # Force redirect HTTP -> HTTPS. 43 if not flask.request.is_secure and not is_local_testing_instance: 44 https_url = flask.request.url.replace('http://', 'https://', 1) 45 return flask.redirect(https_url, code=301) 46 if flask.request.host == 'www.perfetto.dev': 47 return flask.redirect( 48 flask.request.url.replace('www.perfetto.dev', 'perfetto.dev')) 49 if flask.request.host == 'docs.perfetto.dev': 50 return flask.redirect('https://perfetto.dev/docs/') 51 52 path = '/' + path 53 path += 'index.html' if path.endswith('/') else '' 54 global bucket 55 if bucket is None: 56 bucket = storage.Client().get_bucket(BUCKET_NAME) 57 blob = bucket.get_blob(path[1:]) 58 if blob is None: 59 return flask.abort(404) 60 data = blob.download_as_bytes() 61 resp = flask.Response(data) 62 resp.headers['Content-Type'] = blob.content_type 63 resp.headers['Content-Length'] = len(data) 64 resp.headers['Content-Encoding'] = blob.content_encoding 65 if os.path.splitext(path)[1] in ('.png', '.svg'): 66 resp.headers['Cache-Control'] = 'public, max-age=86400' # 1 Day 67 else: 68 resp.headers['Cache-Control'] = 'public, max-age=600' # 10 min 69 return resp 70 71 72def get_credentials_for_local_testing(): 73 from google_auth_oauthlib import flow 74 flow = flow.InstalledAppFlow.from_client_config( 75 client_config={ 76 'installed': { 77 # These aren't secret. Copied from gsutil's apitools sources. 78 'client_id': '1042881264118.apps.googleusercontent.com', 79 'client_secret': 'x_Tw5K8nnjoRAqULM9PFAC2b', 80 'redirect_uris': ['urn:ietf:wg:oauth:2.0:oob'], 81 'auth_uri': 'https://accounts.google.com/o/oauth2/auth', 82 'token_uri': 'https://accounts.google.com/o/oauth2/token' 83 } 84 }, 85 scopes=['https://www.googleapis.com/auth/devstorage.read_only']) 86 creds = flow.run_console() 87 return creds 88 89 90if __name__ == '__main__': 91 # This is used when running locally only. 92 creds = get_credentials_for_local_testing() 93 storage_client = storage.Client(project='perfetto-site', credentials=creds) 94 bucket = storage_client.bucket(BUCKET_NAME) 95 is_local_testing_instance = True 96 app.run(host='127.0.0.1', port=8082, debug=False) 97