qiniu_multipart/server/
hyper.rs

1// Copyright 2016 `multipart` Crate Developers
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7//! Server-side integration with [Hyper](https://github.com/hyperium/hyper).
8//! Enabled with the `hyper` feature (on by default).
9//!
10//! Also contains an implementation of [`HttpRequest`](../trait.HttpRequest.html)
11//! for `hyper::server::Request` and `&mut hyper::server::Request`.
12use hyper::net::Fresh;
13use hyper::header::ContentType;
14use hyper::method::Method;
15use hyper::server::{Handler, Request, Response};
16
17pub use hyper::server::Request as HyperRequest;
18
19use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};
20
21use super::{Multipart, HttpRequest};
22
23/// A container that implements `hyper::server::Handler` which will switch
24/// the handler implementation depending on if the incoming request is multipart or not.
25///
26/// Create an instance with `new()` and pass it to `hyper::server::Server::listen()` where
27/// you would normally pass a `Handler` instance.
28///
29/// A convenient wrapper for `Multipart::from_request()`.
30pub struct Switch<H, M> {
31    normal: H,
32    multipart: M,
33}
34
35impl<H, M> Switch<H, M> where H: Handler, M: MultipartHandler {
36    /// Create a new `Switch` instance where
37    /// `normal` handles normal Hyper requests and `multipart` handles Multipart requests
38    pub fn new(normal: H, multipart: M) -> Switch<H, M> {
39        Switch { normal, multipart }
40    }
41}
42
43impl<H, M> Handler for Switch<H, M> where H: Handler, M: MultipartHandler {
44    fn handle<'a, 'k>(&'a self, req: Request<'a, 'k>, res: Response<'a, Fresh>) {
45        match Multipart::from_request(req) {
46            Ok(multi) => self.multipart.handle_multipart(multi, res),
47            Err(req) => self.normal.handle(req, res),
48        }
49    }
50}
51
52/// A trait defining a type that can handle an incoming multipart request.
53///
54/// Extends to closures of the type `Fn(Multipart<Request>, Response<Fresh>)`,
55/// and subsequently static functions.
56pub trait MultipartHandler: Send + Sync {
57    /// Generate a response from this multipart request.
58    fn handle_multipart<'a, 'k>(&self, 
59                                multipart: Multipart<Request<'a, 'k>>, 
60                                response: Response<'a, Fresh>);
61}
62
63impl<F> MultipartHandler for F 
64where F: Fn(Multipart<Request>, Response<Fresh>), F: Send + Sync {
65    fn handle_multipart<'a, 'k>(&self, 
66                                multipart: Multipart<Request<'a, 'k>>, 
67                                response: Response<'a, Fresh>) {
68        (*self)(multipart, response);
69    }
70}
71
72impl<'a, 'b> HttpRequest for HyperRequest<'a, 'b> {
73    type Body = Self;
74
75    fn multipart_boundary(&self) -> Option<&str> {
76        if self.method != Method::Post {
77            return None;
78        }
79
80        self.headers.get::<ContentType>().and_then(|ct| {
81            let ContentType(ref mime) = *ct;
82            let params = match *mime {
83                Mime(TopLevel::Multipart, SubLevel::FormData, ref params) => params,
84                _ => return None,
85            };
86
87            params.iter().find(|&&(ref name, _)|
88                match *name {
89                    Attr::Boundary => true,
90                    _ => false,
91                }
92            ).and_then(|&(_, ref val)|
93                match *val {
94                    Value::Ext(ref val) => Some(&**val),
95                    _ => None,
96                }
97            )
98        })
99    }
100
101    fn body(self) -> Self {
102        self
103    }
104}
105
106impl<'r, 'a, 'b> HttpRequest for &'r mut HyperRequest<'a, 'b> {
107    type Body = Self;
108
109    fn multipart_boundary(&self) -> Option<&str> {
110        if self.method != Method::Post {
111            return None;
112        }
113
114        self.headers.get::<ContentType>().and_then(|ct| {
115            let ContentType(ref mime) = *ct;
116            let params = match *mime {
117                Mime(TopLevel::Multipart, SubLevel::FormData, ref params) => params,
118                _ => return None,
119            };
120
121            params.iter().find(|&&(ref name, _)|
122                match *name {
123                    Attr::Boundary => true,
124                    _ => false,
125                }
126            ).and_then(|&(_, ref val)|
127                match *val {
128                    Value::Ext(ref val) => Some(&**val),
129                    _ => None,
130                }
131            )
132        })
133    }
134
135    fn body(self) -> Self::Body {
136        self
137    }
138}
139