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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
//! Module that deals with a web server.
//!
//! See [run_web](fn.run_web.html) documentation for details.
use DbInterface;
use HttpResult;
use Duration;
use Listening;
use *;
use Pastebin;
use ToSocketAddrs;
use Tera;
/// Runs a web server.
///
/// This is the main function of the library. Starts a web server and serves the
/// following HTTP requests: `GET`, `POST`, `PUT` and `DELETE`.
///
/// Basically it is just a layer between an `Iron` web server and a `DbInterface` implementation.
///
/// The call returns a `HttpResult` which comes directly from `Iron`, which means you can possibly
/// terminate the server in a clean way. If you don't `close` it explicitly, the object will hang
/// forever in its `drop` implementation. For more details have a look at the
/// `iron::error::HttpResult` documentation.
///
/// # Arguments
///
/// * `db_wrapper` is a layer that provides an access to your favourite database. Must implement
/// `DbInterface` and have a `'static` lifetime.
///
/// * `addr` is a local address which the webserver will use. Rust provides a very nice way to
/// handle it, please go ahead and read docs regarding the `ToSocketAddrs` trait, but if you need a
/// fast solution just pass a string like `"0.0.0.0:8000"` to make the server to listen to incoming
/// requests on port 8000 on all the available network interfaces.
///
/// * `templates` is an instance of the [Tera](https://github.com/Keats/tera) template engine.
/// Please refer to the following section to learn the requirements.
///
/// * `url_prefix` used for responding to `POST`/`PUT` requests: if a paste has been successfully
/// inserted into the database the server will reply with the following string: `${addr}id` (please
/// mind that `addr` will always end with a slash `/`), where `${addr}` is that url prefix (with a
/// slash…). So you probably want to put an external address of your paste service instance ;-).
///
/// * `default_ttl` represents the default expiration time which will be applied if not `expires`
/// argument for a `POST`/`PUT` request is given.
///
/// * `static_files_path` is a path relative to the working path (i.e. the path where you have
/// launched the service). As the name suggests it will be used to server static files that reside
/// in that directory. As for now, *sub-directories are not supported*, that is you can't serve
/// files that reside not directly at the path. To access a static file use a `GET` request on the
/// address `/<file-name>`, very simple and straightforward.
///
/// # Templates
///
/// The service uses `Tera` templates to build web pages, so it expects the engine to serve the
/// following files:
///
/// * `show.html.tera`: expects `id` (a paste id), `mime` (mime-type string), `file_name` (`null`
/// if there is no file name associated with the paste), and `data` which is actually the paste
/// itself.
/// * `upload.html.tera`: no parameters.
/// * `paste.sh.tera`: expects `prefix`, see `url_prefix` argument.
/// * `readme.html.tera`: also expects `prefix`.
///
/// All these files are provided with the service (`/templates/`).
///
/// # Notice
///
/// No matter how many ending slashes (`/`) you add to `url_prefix` (even zero), all of them will be
/// removed and one slash will be added. If for some reason you need two (or more) ending slashes
/// feel free to post an issue on github, because this is basically a hack and could be (and
/// probably should be) properly fixed.
///
/// # `PUT` vs `POST`
///
/// While [REST](https://en.wikipedia.org/wiki/Representational_state_transfer) differentiates
/// between those two request kinds, there is no difference in this service. Why? Well, just
/// because some CLI clients tend to use `POST` requests by default for sending data and some use
/// `PUT`, so that's why the service do not care. If you have any argument why this shouldn't be
/// the case please fill free to post an issue on github.
///
/// # Example
///
/// Let's say you have some kind of a database wrapper implemented (`DbImplementation`) and you
/// want to run a server with it:
///
/// ```
/// # extern crate pastebin;
/// # extern crate bson;
/// # extern crate chrono;
/// # use pastebin::{DbInterface, PasteEntry};
/// # use std::io;
/// # use chrono::{DateTime, Duration, Utc};
/// # struct DbImplementation;
/// # impl DbInterface for DbImplementation {
/// # type Error = io::Error;
/// # fn store_data(&self,
/// # _data: Vec<u8>,
/// # _file_name: Option<String>,
/// # _mime_type: String,
/// # _best_before: Option<DateTime<Utc>>)
/// # -> Result<u64, Self::Error> {
/// # unimplemented!()
/// # }
/// # fn load_data(&self, _: u64) -> Result<Option<PasteEntry>, Self::Error> {
/// # unimplemented!()
/// # }
/// # fn get_file_name(&self, _: u64) -> Result<Option<String>, Self::Error> {
/// # unimplemented!()
/// # }
/// # fn remove_data(&self, _: u64) -> Result<(), Self::Error> {
/// # unimplemented!()
/// # }
/// # fn max_data_size(&self) -> usize {
/// # unimplemented!()
/// # }
/// # }
/// # impl DbImplementation {
/// # fn new() -> Self { Self{} }
/// # }
/// # fn main() {
/// let mut web = pastebin::web::run_web(
/// DbImplementation::new(/* ... */),
/// "127.0.0.1:8000",
/// // ...
/// # Default::default(),
/// # Default::default(),
/// # Duration::zero(),
/// # Default::default(),
/// ).unwrap();
/// // ... do something ...
/// web.close(); // Graceful termination.
/// println!("Server terminated, exiting");
/// # }
/// ```
///
/// Simple, isn't it? It can be even simplier if you don't care about graceful termination and
/// you're okay with the application working forever:
///
/// ```no_run
/// # extern crate pastebin;
/// # extern crate bson;
/// # extern crate chrono;
/// # use pastebin::{DbInterface, PasteEntry};
/// # use std::io;
/// # use chrono::{DateTime, Duration, Utc};
/// # struct DbImplementation;
/// # impl DbInterface for DbImplementation {
/// # type Error = io::Error;
/// # fn store_data(&self,
/// # _data: Vec<u8>,
/// # _file_name: Option<String>,
/// # _mime_type: String,
/// # _best_before: Option<DateTime<Utc>>)
/// # -> Result<u64, Self::Error> {
/// # unimplemented!()
/// # }
/// # fn load_data(&self, _: u64) -> Result<Option<PasteEntry>, Self::Error> {
/// # unimplemented!()
/// # }
/// # fn get_file_name(&self, _: u64) -> Result<Option<String>, Self::Error> {
/// # unimplemented!()
/// # }
/// # fn remove_data(&self, _: u64) -> Result<(), Self::Error> {
/// # unimplemented!()
/// # }
/// # fn max_data_size(&self) -> usize {
/// # unimplemented!()
/// # }
/// # }
/// # impl DbImplementation {
/// # fn new() -> Self { Self{} }
/// # }
/// # fn main() {
/// pastebin::web::run_web(
/// DbImplementation::new(/* ... */),
/// "127.0.0.1:8000",
/// // ...
/// # Default::default(),
/// # Default::default(),
/// # Duration::zero(),
/// # Default::default(),
/// ).unwrap();
/// println!("Ok done"); // <-- will never be reached.
/// # }
/// ```