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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
//! Support for `multipart/form-data` bodies in [Nickel](https://nickel.rs) via the
//! [`multipart`](https://crates.io/crates/multipart) crate.
//!
//! ### Why an external crate?
//! Three major reasons led to the decision to move Nickel integration to an external crate.
//!
//! 1: The part of Nickel's public API that matters to `multipart` (getting headers and
//! reading the request) uses Hyper's request type; this means that Nickel's integration
//! must necessarily be coupled to Hyper's integration.
//!
//! 2: Cargo does not allow specifying two different versions of the same crate in the
//! same manifest, probably for some good reasons but this crate's author has not looked into it.
//!
//! 3: Nickel's development moves incredibly slowly; when a new version of Hyper releases, there is
//! always a significant lag before Nickel upgrades,
//! [sometimes several months](https://github.com/nickel-org/nickel.rs/issues/367)--in this case,
//! Hyper was upgraded (in May) two months before the issue was opened (July), but a new version of
//! Nickel was not published until four months later (September).
//!
//! This causes problems for `multipart` because it cannot upgrade Hyper until Nickel does,
//! but its Hyper users often want to upgrade their Hyper as soon as possible.
//!
//! In order to provide up-to-date integration for Hyper, it was necessary to move Nickel
//! integration to an external crate so it can be pinned at the version of Hyper that Nickel
//! supports. This allows `multipart` to upgrade Hyper arbitrarily and still keep everyone happy.
//!
//! ### Porting from `multipart`'s Integration
//!
//! Whereas `multipart` only provided one way to wrap a Nickel request, this crate provides two:
//!
//! * To continue using `Multipart::from_request()`, wrap the request in
//! [`Maybe`](struct.Maybe.html):
//!
//! ```ignore
//! // Where `req` is `&mut nickel::Request`
//! - Multipart::from_request(req)
//! + use multipart_nickel::Maybe;
//! + Multipart::from_request(Maybe(req))
//! ```
//!
//! * Import `multipart_nickel::MultipartBody` and call `.multipart_body()`, which returns
//! `Option` (which better matches the conventions of `nickel::FormBody` and `nickel::JsonBody`):
//!
//! ```rust,ignore
//! use multipart_nickel::MultipartBody;
//!
//! // Where `req` is `&mut nickel::Request`
//! match req.multipart_body() {
//!     Some(multipart) => // handle multipart body
//!     None => // handle regular body
//! }
//! ```
//!
//! This crate also reexports the `server` module from the version of `multipart`
//! that it uses, so that you don't have to import `multipart` separately.
extern crate hyper;
extern crate multipart;
extern crate nickel;

use nickel::Request as NickelRequest;

use hyper::server::Request as HyperRequest;

/// The `server` module from the version of `multipart` that this crate uses.
pub use multipart::server as multipart_server;

use multipart_server::{HttpRequest, Multipart};

/// A wrapper for `&mut nickel::Request` which implements `multipart::server::HttpRequest`.
///
/// Necessary because this crate cannot directly provide an impl of `HttpRequest` for
/// `&mut NickelRequest`.
pub struct Maybe<'r, 'mw: 'r, 'server: 'mw, D: 'mw>(pub &'r mut NickelRequest<'mw, 'server, D>);

impl<'r, 'mw: 'r, 'server: 'mw, D: 'mw> HttpRequest for Maybe<'r, 'mw, 'server, D> {
    type Body = &'r mut HyperRequest<'mw, 'server>;

    fn multipart_boundary(&self) -> Option<&str> {
        self.0.origin.multipart_boundary()
    }

    fn body(self) -> Self::Body {
        &mut self.0.origin
    }
}

/// Extension trait for getting the `multipart/form-data` body from `nickel::Request`.
///
/// Implemented for `nickel::Request`.
pub trait MultipartBody<'mw, 'server> {
    /// Get a multipart reader for the request body, if the request is of the right type.
    fn multipart_body(&mut self) -> Option<Multipart<&mut HyperRequest<'mw, 'server>>>;
}

impl<'mw, 'server, D: 'mw> MultipartBody<'mw, 'server> for NickelRequest<'mw, 'server, D> {
    fn multipart_body(&mut self) -> Option<Multipart<&mut HyperRequest<'mw, 'server>>> {
        Multipart::from_request(Maybe(self)).ok()
    }
}


impl<'r, 'mw: 'r, 'server: 'mw, D: 'mw> AsRef<&'r mut NickelRequest<'mw, 'server, D>> for Maybe<'r, 'mw, 'server, D> {
    fn as_ref(&self) -> &&'r mut NickelRequest<'mw, 'server, D> {
        &self.0
    }
}

impl<'r, 'mw: 'r, 'server: 'mw, D: 'mw> AsMut<&'r mut NickelRequest<'mw, 'server, D>> for Maybe<'r, 'mw, 'server, D> {
    fn as_mut(&mut self) -> &mut &'r mut NickelRequest<'mw, 'server, D> {
        &mut self.0
    }
}

impl<'r, 'mw: 'r, 'server: 'mw, D: 'mw> Into<&'r mut NickelRequest<'mw, 'server, D>> for Maybe<'r, 'mw, 'server, D> {
    fn into(self) -> &'r mut NickelRequest<'mw, 'server, D> {
        self.0
    }
}

impl<'r, 'mw: 'r, 'server: 'mw, D: 'mw> From<&'r mut NickelRequest<'mw, 'server, D>> for Maybe<'r, 'mw, 'server, D> {
    fn from(req: &'r mut NickelRequest<'mw, 'server, D>) -> Self {
        Maybe(req)
    }
}