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