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
100
101
102
103
104
105
106
107
108
109
110
use crate::{Agent, Response};
use url::{form_urlencoded::Serializer, Url};
use worker::{
    wasm_bindgen::JsValue,
    worker_sys::{
        Request as EdgeRequest, RequestInit as EdgeRequestInit,
        RequestRedirect as EdgeRequestRedirect,
    },
    Error, Fetch, Headers, Method, Request as WorkerRequest, Response as WorkerResponse,
};

pub struct Get;
pub struct Post;

pub struct Request<M> {
    agent: Agent,
    url: Url,
    method: Method,
    headers: Headers,
    _marker: M,
}

impl<M> Request<M> {
    pub(crate) fn new(agent: Agent, url: Url, method: Method, _marker: M) -> Self {
        let mut headers = Headers::new();
        let cookies = agent.get_request_cookies(&url);
        headers.set("Cookie", &cookies).unwrap();

        Self {
            agent,
            url,
            method,
            headers,
            _marker,
        }
    }

    pub fn url(&self) -> &Url {
        &self.url
    }

    pub fn headers(&mut self) -> &mut Headers {
        &mut self.headers
    }

    async fn do_call(self, body: Option<&JsValue>) -> Result<Response, Error> {
        let mut resp = self.get_response_inner(body).await?;
        let mut url = self.url;

        loop {
            if resp.status_code() != 301 && resp.status_code() != 302 {
                if let Some(cookies) = resp.headers().get("Set-Cookie")? {
                    self.agent.store_response_cookies(&url, &cookies);
                }

                return Ok(Response::new(resp, url));
            } else if let Some(redir_url) = resp
                .headers()
                .get("Location")?
                .and_then(|location| url.join(&location).ok())
            {
                if let Some(cookies) = resp.headers().get("Set-Cookie")? {
                    self.agent.store_response_cookies(&redir_url, &cookies);
                }

                let req = self.agent.get(redir_url);
                resp = req.get_response_inner(None).await?;
                url = req.url;
            } else {
                if let Some(cookies) = resp.headers().get("Set-Cookie")? {
                    self.agent.store_response_cookies(&url, &cookies);
                }

                return Ok(Response::new(resp, url));
            }
        }
    }

    async fn get_response_inner(&self, body: Option<&JsValue>) -> Result<WorkerResponse, Error> {
        let mut init = EdgeRequestInit::new();

        init.method(self.method.as_ref())
            .headers(&self.headers.0)
            .redirect(EdgeRequestRedirect::Manual)
            .body(body);

        let req = EdgeRequest::new_with_str_and_init(self.url.as_str(), &init)?;
        Fetch::Request(WorkerRequest::from(req)).send().await
    }
}

impl Request<Get> {
    pub async fn call(self) -> Result<Response, Error> {
        self.do_call(None).await
    }
}

impl Request<Post> {
    pub async fn send_form(
        mut self,
        form: impl IntoIterator<Item = (impl AsRef<str>, impl AsRef<str>)>,
    ) -> Result<Response, Error> {
        self.headers
            .set("Content-Type", "application/x-www-form-urlencoded")?;

        let form = JsValue::from(Serializer::new(String::new()).extend_pairs(form).finish());

        self.do_call(Some(&form)).await
    }
}