traq_bot_http/parser/
http.rs

1// #![cfg(feature = "http")]
2
3use std::future::Future;
4use std::pin::Pin;
5use std::task::{Context, Poll};
6
7use bytes::Bytes;
8use futures_core::ready;
9use futures_util::future::Ready;
10use http_body::Body;
11use http_body_util::{combinators::Collect, Collected};
12use pin_project_lite::pin_project;
13
14use crate::error::{Error, Result};
15use crate::events::{Event, EventKind};
16use crate::parser::RequestParser;
17
18pin_project! {
19    #[must_use]
20    #[project = CollectBodyProject]
21    struct CollectBody<B>
22    where
23        B: Body,
24        B: ?Sized,
25    {
26        #[pin]
27        collect: Collect<B>,
28    }
29}
30
31impl<B> Future for CollectBody<B>
32where
33    B: Body + ?Sized,
34    B::Error: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
35{
36    type Output = Result<Bytes>;
37
38    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
39        let s = self.project();
40        let collected = ready!(s.collect.poll(cx));
41        let res = collected
42            .map(Collected::to_bytes)
43            .map_err(Error::read_body_failed);
44        Poll::Ready(res)
45    }
46}
47
48pin_project! {
49    #[must_use]
50    #[project = ParseEventKindProject]
51    struct ParseEventKind<K, B> {
52        #[pin]
53        inner: K,
54        body: Option<B>
55    }
56}
57
58impl<K, B> Future for ParseEventKind<K, B>
59where
60    K: Future<Output = Result<EventKind>>,
61{
62    type Output = ParseRequestInner<K, B>;
63
64    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
65        let s = self.project();
66        let res = ready!(s.inner.poll(cx));
67        let next = match res {
68            Ok(kind) => {
69                let body = s.body.take().expect("polled after ready");
70                ParseRequestInner::ParseBody {
71                    inner: ParseBody { kind, inner: body },
72                }
73            }
74            Err(e) => ParseRequestInner::ParseEventKindFailed {
75                inner: futures_util::future::ready(Err(e)),
76            },
77        };
78        Poll::Ready(next)
79    }
80}
81
82type ParseEventKindFailed = Ready<Result<Event>>;
83
84pin_project! {
85    #[must_use]
86    #[project = ParseBodyProject]
87    struct ParseBody<B> {
88        kind: EventKind,
89        #[pin]
90        inner: B,
91    }
92}
93
94impl<B> Future for ParseBody<B>
95where
96    B: Future<Output = Result<Bytes>>,
97{
98    type Output = Result<Event>;
99
100    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
101        let s = self.project();
102        let body = ready!(s.inner.poll(cx));
103        let res: Result<Event> = {
104            let body = body?;
105            let body = std::str::from_utf8(&body).map_err(Error::read_body_failed)?;
106            super::parse_body(*s.kind, body)
107        };
108        Poll::Ready(res)
109    }
110}
111
112pin_project! {
113    #[must_use]
114    #[project = ParseRequestInnerProject]
115    #[project_replace = ParseRequestInnerProjectReplace]
116    enum ParseRequestInner<K, B> {
117        ParseEventKind {
118            #[pin]
119            inner: ParseEventKind<K, B>,
120        },
121        ParseEventKindFailed {
122            #[pin]
123            inner: ParseEventKindFailed,
124        },
125        ParseBody {
126            #[pin]
127            inner: ParseBody<B>,
128        }
129    }
130}
131
132impl<K, B> ParseRequestInner<K, B>
133where
134    K: Future<Output = Result<EventKind>>,
135    B: Future<Output = Result<Bytes>>,
136{
137    fn new(kind: K, body: B) -> Self {
138        Self::ParseEventKind {
139            inner: ParseEventKind {
140                inner: kind,
141                body: Some(body),
142            },
143        }
144    }
145}
146
147impl<K, B> Future for ParseRequestInner<K, B>
148where
149    K: Future<Output = Result<EventKind>>,
150    B: Future<Output = Result<Bytes>>,
151{
152    type Output = Result<Event>;
153
154    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
155        use ParseRequestInnerProject::{ParseBody, ParseEventKind, ParseEventKindFailed};
156        let s = self.as_mut().project();
157        let next = match s {
158            ParseEventKind { inner } => ready!(inner.poll(cx)),
159            ParseEventKindFailed { inner } => return inner.poll(cx),
160            ParseBody { inner } => return inner.poll(cx),
161        };
162        self.project_replace(next);
163        cx.waker().wake_by_ref();
164        Poll::Pending
165    }
166}
167
168pin_project! {
169    /// <code>impl [Future]<Output = Result<[Event], [Error]>></code>
170    ///
171    /// [Future]: std::future::Future
172    /// [Event]: crate::Event
173    /// [Error]: crate::Error
174    #[must_use]
175    #[project = ParseRequestProject]
176    pub struct ParseRequest<B>
177    where
178        B: Body,
179    {
180        #[pin]
181        inner: ParseRequestInner<Ready<Result<EventKind>>, CollectBody<B>>
182    }
183}
184
185impl<B> ParseRequest<B>
186where
187    B: Body,
188    B::Error: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
189{
190    fn new(kind: Result<EventKind>, body: B) -> Self {
191        use http_body_util::BodyExt;
192
193        let kind = futures_util::future::ready(kind);
194        let body = CollectBody {
195            collect: body.collect(),
196        };
197        let inner = ParseRequestInner::new(kind, body);
198        Self { inner }
199    }
200}
201
202impl<B> Future for ParseRequest<B>
203where
204    B: Body,
205    B::Error: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
206{
207    type Output = Result<Event>;
208
209    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
210        let s = self.project();
211        s.inner.poll(cx)
212    }
213}
214
215impl RequestParser {
216    /// [`http::Request`]をパースします。
217    ///
218    /// **Note**: この関数は`http`featureが有効になっている時のみ有効です。
219    ///
220    /// # Arguments
221    ///
222    /// * `request`: リクエスト全体
223    ///
224    /// # Example
225    ///
226    /// ```
227    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
228    /// # let res: Result<(), Box<dyn std::error::Error>> = futures::executor::block_on(async {
229    /// use traq_bot_http::{EventKind, RequestParser};
230    ///
231    /// let verification_token = "verification_token";
232    /// let body = r#"{"eventTime": "2019-05-07T04:50:48.582586882Z"}"#.to_string();
233    /// let request = http::Request::builder()
234    ///     .method(http::Method::POST)
235    ///     .header(http::header::CONTENT_TYPE, "application/json")
236    ///     .header("X-TRAQ-BOT-TOKEN", verification_token)
237    ///     .header("X-TRAQ-BOT-EVENT", "PING")
238    ///     .body(body)?;
239    /// let parser = RequestParser::new(verification_token);
240    /// let event = parser.parse_request(request).await?;
241    /// assert_eq!(event.kind(), EventKind::Ping);
242    /// # Ok(())
243    /// # });
244    /// # res
245    /// # }
246    /// ```
247    ///
248    /// # Errors
249    ///
250    /// [`Error`]のうち、[`Error::kind`]が以下のものを返す可能性があります。
251    ///
252    /// - [`parse`]で返されるもの
253    /// - [`ErrorKind::ReadBodyFailed`] :
254    ///     リクエストボディの読み込みに失敗した
255    ///
256    /// [`Error::kind`]: crate::Error::kind
257    /// [`parse`]: crate::RequestParser::parse
258    /// [`ErrorKind::ReadBodyFailed`]: crate::ErrorKind::ReadBodyFailed
259    pub fn parse_request<B>(&self, request: http::Request<B>) -> ParseRequest<B>
260    where
261        B: Body,
262        B::Error: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
263    {
264        let (parts, body) = request.into_parts();
265        let kind = self.parse_headers(&parts.headers);
266        ParseRequest::new(kind, body)
267    }
268}
269
270#[cfg(test)]
271mod tests {
272    use futures::executor::block_on;
273    use http_body_util::BodyExt;
274
275    use super::{CollectBody, ParseRequest};
276    use crate::{Error, ErrorKind, Event, EventKind};
277
278    #[test]
279    fn collect_body() {
280        let body_content = "some content";
281        let fut = CollectBody {
282            collect: body_content.to_string().collect(),
283        };
284        let collected = block_on(fut).unwrap();
285        assert_eq!(collected, body_content.as_bytes());
286    }
287
288    #[test]
289    fn parse_request_future() {
290        let kind = EventKind::Ping;
291        let payload = r#"{"eventTime": "2019-05-07T04:50:48.582586882Z"}"#;
292        let body = payload.to_string();
293        let fut = ParseRequest::new(Ok(kind), body);
294        let event = block_on(fut).unwrap();
295        assert!(matches!(event, Event::Ping(_)));
296    }
297
298    #[test]
299    fn parse_event_failed() {
300        let err: Error = ErrorKind::BotTokenMismatch.into();
301        let body = String::new();
302        let fut = ParseRequest::new(Err(err), body);
303        let err = block_on(fut).unwrap_err();
304        assert_eq!(err.kind(), ErrorKind::BotTokenMismatch);
305    }
306}