1// Copyright (C) 2020 The Android Open Source Project 2// 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 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 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. 14 15import {RecordConfig} from '../controller/record_config_types'; 16 17export const BUCKET_NAME = 'perfetto-ui-data'; 18import {v4 as uuidv4} from 'uuid'; 19import {State} from './state'; 20 21export async function saveTrace(trace: File|ArrayBuffer): Promise<string> { 22 // TODO(hjd): This should probably also be a hash but that requires 23 // trace processor support. 24 const name = uuidv4(); 25 const url = 'https://www.googleapis.com/upload/storage/v1/b/' + 26 `${BUCKET_NAME}/o?uploadType=media` + 27 `&name=${name}&predefinedAcl=publicRead`; 28 const response = await fetch(url, { 29 method: 'post', 30 headers: {'Content-Type': 'application/octet-stream;'}, 31 body: trace, 32 }); 33 await response.json(); 34 return `https://storage.googleapis.com/${BUCKET_NAME}/${name}`; 35} 36 37// Bigint's are not serializable using JSON.stringify, so we use a special 38// object when serialising 39export type SerializedBigint = { 40 __kind: 'bigint', 41 value: string 42}; 43 44// Check if a value looks like a serialized bigint 45export function isSerializedBigint(value: unknown): value is SerializedBigint { 46 if (value === null) { 47 return false; 48 } 49 if (typeof value !== 'object') { 50 return false; 51 } 52 if ('__kind' in value && 'value' in value) { 53 return value.__kind === 'bigint' && typeof value.value === 'string'; 54 } 55 return false; 56} 57 58export function serializeStateObject(object: unknown): string { 59 const json = JSON.stringify(object, (key, value) => { 60 if (typeof value === 'bigint') { 61 return { 62 __kind: 'bigint', 63 value: value.toString(), 64 }; 65 } 66 return key === 'nonSerializableState' ? undefined : value; 67 }); 68 return json; 69} 70 71export function deserializeStateObject(json: string): any { 72 const object = JSON.parse(json, (_key, value) => { 73 if (isSerializedBigint(value)) { 74 return BigInt(value.value); 75 } 76 return value; 77 }); 78 return object; 79} 80 81export async function saveState(stateOrConfig: State| 82 RecordConfig): Promise<string> { 83 const text = serializeStateObject(stateOrConfig); 84 const hash = await toSha256(text); 85 const url = 'https://www.googleapis.com/upload/storage/v1/b/' + 86 `${BUCKET_NAME}/o?uploadType=media` + 87 `&name=${hash}&predefinedAcl=publicRead`; 88 const response = await fetch(url, { 89 method: 'post', 90 headers: { 91 'Content-Type': 'application/json; charset=utf-8', 92 }, 93 body: text, 94 }); 95 await response.json(); 96 return hash; 97} 98 99// This has a bug: 100// x.toString(16) doesn't zero pad so if the digest is: 101// [23, 7, 42, ...] 102// You get: 103// ['17', '7', '2a', ...] = 1772a... 104// Rather than: 105// ['17', '07', '2a', ...] = 17072a... 106// As you ought to (and as the hexdigest is computed by e.g. Python). 107// Unfortunately there are a lot of old permalinks out there so we 108// still need this broken implementation to check their hashes. 109export async function buggyToSha256(str: string): Promise<string> { 110 const buffer = new TextEncoder().encode(str); 111 const digest = await crypto.subtle.digest('SHA-256', buffer); 112 return Array.from(new Uint8Array(digest)).map((x) => x.toString(16)).join(''); 113} 114 115export async function toSha256(str: string): Promise<string> { 116 const buffer = new TextEncoder().encode(str); 117 const digest = await crypto.subtle.digest('SHA-256', buffer); 118 return Array.from(new Uint8Array(digest)) 119 .map((x) => x.toString(16).padStart(2, '0')) 120 .join(''); 121} 122