1/* 2 * Copyright (C) 2024 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 */ 16 17import {assertDefined} from './assert_utils'; 18 19/** 20 * Type for HTTP request headers. 21 */ 22export type HttpRequestHeaderType = Array<[string, string]>; 23 24/** 25 * Status of an HTTP request. 26 */ 27export enum HttpRequestStatus { 28 UNSENT, 29 UNAUTH, 30 SUCCESS, 31 ERROR, 32} 33 34/** 35 * Response from an HTTP request. 36 */ 37export interface HttpResponse { 38 status: HttpRequestStatus; 39 type: XMLHttpRequestResponseType; //eslint-disable-line no-undef 40 text: string; 41 body: any; 42 getHeader: (name: string) => string | undefined; 43} 44 45/** 46 * Class for making HTTP requests. 47 */ 48export class HttpRequest { 49 /** 50 * Make a GET request. 51 * @param path The path of the request. 52 * @param headers The headers of the request. 53 * @param type The response type of the request. 54 * @return A promise that resolves to the response. 55 */ 56 static async get( 57 path: string, 58 headers: HttpRequestHeaderType, 59 type?: XMLHttpRequest['responseType'], 60 ): Promise<HttpResponse> { 61 return await HttpRequest.call('GET', path, headers, type); 62 } 63 64 /** 65 * Make a POST request. 66 * @param path The path of the request. 67 * @param headers The headers of the request. 68 * @param jsonRequest The JSON request body. 69 * @return A promise that resolves to the response. 70 */ 71 static async post( 72 path: string, 73 headers: HttpRequestHeaderType, 74 jsonRequest?: object, 75 ): Promise<HttpResponse> { 76 return await HttpRequest.call( 77 'POST', 78 path, 79 headers, 80 undefined, 81 jsonRequest, 82 ); 83 } 84 85 private static async call( 86 method: string, 87 path: string, 88 headers: HttpRequestHeaderType, 89 type?: XMLHttpRequest['responseType'], 90 jsonRequest?: object, 91 ): Promise<HttpResponse> { 92 const req = new XMLHttpRequest(); 93 let status: HttpRequestStatus | undefined; 94 95 await new Promise<void>((resolve) => { 96 req.onreadystatechange = async () => { 97 if (req.readyState !== XMLHttpRequest.DONE) { 98 return; 99 } 100 if (req.status === XMLHttpRequest.UNSENT) { 101 status = HttpRequestStatus.UNSENT; 102 } else if (req.status === 200) { 103 status = HttpRequestStatus.SUCCESS; 104 } else if (req.status === 403) { 105 status = HttpRequestStatus.UNAUTH; 106 } else { 107 status = HttpRequestStatus.ERROR; 108 } 109 resolve(); 110 }; 111 req.responseType = type || ''; 112 req.open(method, path); 113 headers.forEach(([header, value]) => { 114 req.setRequestHeader(header, value); 115 }); 116 117 if (jsonRequest) { 118 const json = JSON.stringify(jsonRequest); 119 req.setRequestHeader('Content-Type', 'application/json;charset=UTF-8'); 120 req.send(json); 121 } else { 122 req.send(); 123 } 124 }); 125 126 const hasResponseText = 127 req.responseType === '' || req.responseType === 'text'; 128 129 return { 130 status: assertDefined(status), 131 type: req.responseType, 132 text: hasResponseText ? req.responseText : '', 133 body: req.response, 134 getHeader: (name: string) => req.getResponseHeader(name) ?? undefined, 135 }; 136 } 137} 138