1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
use std::io::{Error, ErrorKind};

use prost::bytes::Bytes;
use tonic::transport::Channel;

/// Client which interacts with gRPC HTTP service
pub struct Client {
    inner: avalanche_proto::http::http_client::HttpClient<Channel>,
}

impl Client {
    pub fn new(client_conn: Channel) -> Box<dyn crate::rpcchainvm::http::Handler + Send + Sync> {
        Box::new(Client {
            inner: avalanche_proto::http::http_client::HttpClient::new(client_conn),
        })
    }
}

#[tonic::async_trait]
impl crate::rpcchainvm::http::Handler for Client {
    async fn serve_http(
        &mut self,
        _req: http::Request<Vec<u8>>,
    ) -> std::io::Result<http::Response<Vec<u8>>> {
        Err(std::io::Error::new(
            ErrorKind::Other,
            format!("not implemented"),
        ))
    }

    /// http client takes an http request and sends to server.  Does not support websockets.
    async fn serve_http_simple(
        &mut self,
        req: http::Request<Vec<u8>>,
    ) -> std::io::Result<http::Response<Vec<u8>>> {
        let req = get_http_simple_request(req)?;

        let resp = self.inner.handle_simple(req).await.map_err(|e| {
            Error::new(
                ErrorKind::Other,
                format!("handle simple request failed: {:?}", e),
            )
        })?;

        get_http_response(resp.into_inner())
    }
}

/// convert from [http::Request] to [avalanche_proto::http::HandleSimpleHttpRequest]
fn get_http_simple_request(
    req: http::Request<Vec<u8>>,
) -> std::io::Result<avalanche_proto::http::HandleSimpleHttpRequest> {
    let headers = convert_to_proto_headers(req.headers())?;

    Ok(avalanche_proto::http::HandleSimpleHttpRequest {
        method: req.method().to_string(),
        url: req.uri().to_string(),
        body: Bytes::from(req.body().to_owned()),
        headers: headers,
    })
}

/// convert from [avalanche_proto::http::HandleSimpleHttpResponse] to [http::Response]
fn get_http_response(
    resp: avalanche_proto::http::HandleSimpleHttpResponse,
) -> std::io::Result<http::Response<Vec<u8>>> {
    let mut http_resp = http::Response::builder().status(resp.code as u16);

    for header in resp.headers.into_iter() {
        http_resp = http_resp.header(header.key, header.values.concat());
    }

    let http_resp = http_resp
        .body(resp.body.to_vec())
        .map_err(|e| {
            Error::new(
                ErrorKind::Other,
                format!("failed to generate http response {:?}", e),
            )
        })
        .unwrap();
    Ok(http_resp)
}

/// converts [http::HeaderMap] to a vec of elements that avalanche proto can use
fn convert_to_proto_headers(
    headers: &http::HeaderMap<http::HeaderValue>,
) -> std::io::Result<Vec<avalanche_proto::http::Element>> {
    let mut vec_headers: Vec<avalanche_proto::http::Element> =
        Vec::with_capacity(headers.keys_len());
    for (key, value) in headers.into_iter() {
        let element = avalanche_proto::http::Element {
            key: key.to_string(),
            values: vec![String::from_utf8_lossy(value.as_bytes()).to_string()],
        };
        vec_headers.push(element);
    }
    Ok(vec_headers)
}