• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (C) 2019 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 {fetchWithTimeout} from '../base/http_utils';
16import {assertExists} from '../base/logging';
17import {StatusResult} from '../common/protos';
18
19import {Engine, LoadingTracker} from './engine';
20
21export const RPC_URL = 'http://127.0.0.1:9001/';
22export const WS_URL = 'ws://127.0.0.1:9001/websocket';
23
24const RPC_CONNECT_TIMEOUT_MS = 2000;
25
26export interface HttpRpcState {
27  connected: boolean;
28  status?: StatusResult;
29  failure?: string;
30}
31
32export class HttpRpcEngine extends Engine {
33  readonly id: string;
34  errorHandler: (err: string) => void = () => {};
35  private requestQueue = new Array<Uint8Array>();
36  private websocket?: WebSocket;
37  private connected = false;
38
39  constructor(id: string, loadingTracker?: LoadingTracker) {
40    super(loadingTracker);
41    this.id = id;
42  }
43
44  rpcSendRequestBytes(data: Uint8Array): void {
45    if (this.websocket === undefined) {
46      this.websocket = new WebSocket(WS_URL);
47      this.websocket.onopen = () => this.onWebsocketConnected();
48      this.websocket.onmessage = (e) => this.onWebsocketMessage(e);
49      this.websocket.onclose = (e) =>
50          this.errorHandler(`Websocket closed (${e.code}: ${e.reason})`);
51      this.websocket.onerror = (e) =>
52          this.errorHandler(`WebSocket error: ${e}`);
53    }
54
55    if (this.connected) {
56      this.websocket.send(data);
57    } else {
58      this.requestQueue.push(data);  // onWebsocketConnected() will flush this.
59    }
60  }
61
62  private onWebsocketConnected() {
63    for (;;) {
64      const queuedMsg = this.requestQueue.shift();
65      if (queuedMsg === undefined) break;
66      assertExists(this.websocket).send(queuedMsg);
67    }
68    this.connected = true;
69  }
70
71  private onWebsocketMessage(e: MessageEvent) {
72    assertExists(e.data as Blob).arrayBuffer().then(buf => {
73      super.onRpcResponseBytes(new Uint8Array(buf));
74    });
75  }
76
77  static async checkConnection(): Promise<HttpRpcState> {
78    const httpRpcState: HttpRpcState = {connected: false};
79    console.info(
80        `It's safe to ignore the ERR_CONNECTION_REFUSED on ${RPC_URL} below. ` +
81        `That might happen while probing the external native accelerator. The ` +
82        `error is non-fatal and unlikely to be the culprit for any UI bug.`);
83    try {
84      const resp = await fetchWithTimeout(
85          RPC_URL + 'status',
86          {method: 'post', cache: 'no-cache'},
87          RPC_CONNECT_TIMEOUT_MS);
88      if (resp.status !== 200) {
89        httpRpcState.failure = `${resp.status} - ${resp.statusText}`;
90      } else {
91        const buf = new Uint8Array(await resp.arrayBuffer());
92        httpRpcState.connected = true;
93        httpRpcState.status = StatusResult.decode(buf);
94      }
95    } catch (err) {
96      httpRpcState.failure = `${err}`;
97    }
98    return httpRpcState;
99  }
100}
101