@@ -16,7 +16,8 @@ use rustc_serialize::json::{Json, ToJson};
16
16
use std:: collections:: BTreeMap ;
17
17
use iron:: headers:: { Expires , HttpDate , CacheControl , CacheDirective } ;
18
18
use time;
19
-
19
+ use iron:: Handler ;
20
+ use utils;
20
21
21
22
22
23
#[ derive( Debug ) ]
@@ -110,6 +111,10 @@ pub fn rustdoc_redirector_handler(req: &mut Request) -> IronResult<Response> {
110
111
}
111
112
112
113
114
+ /// Serves documentation generated by rustdoc.
115
+ ///
116
+ /// This includes all HTML files for an individual crate, as well as the `search-index.js`, which is
117
+ /// also crate-specific.
113
118
pub fn rustdoc_html_server_handler ( req : & mut Request ) -> IronResult < Response > {
114
119
115
120
let router = extension ! ( req, Router ) ;
@@ -151,34 +156,13 @@ pub fn rustdoc_html_server_handler(req: &mut Request) -> IronResult<Response> {
151
156
return Ok ( file. serve ( ) ) ;
152
157
}
153
158
154
- let ( mut in_head, mut in_body) = ( false , false ) ;
155
-
156
159
let mut content = RustdocPage :: default ( ) ;
157
160
158
161
let file_content = ctry ! ( String :: from_utf8( file. content) ) ;
159
162
160
- for line in file_content. lines ( ) {
161
-
162
- if line. starts_with ( "<head" ) {
163
- in_head = true ;
164
- continue ;
165
- } else if line. starts_with ( "</head" ) {
166
- in_head = false ;
167
- } else if line. starts_with ( "<body" ) {
168
- in_body = true ;
169
- continue ;
170
- } else if line. starts_with ( "</body" ) {
171
- in_body = false ;
172
- }
173
-
174
- if in_head {
175
- content. head . push_str ( & line[ ..] ) ;
176
- content. head . push ( '\n' ) ;
177
- } else if in_body {
178
- content. body . push_str ( & line[ ..] ) ;
179
- content. body . push ( '\n' ) ;
180
- }
181
- }
163
+ let ( head, body) = ctry ! ( utils:: extract_head_and_body( & file_content) ) ;
164
+ content. head = head;
165
+ content. body = body;
182
166
183
167
content. full = file_content;
184
168
let crate_details = cexpect ! ( CrateDetails :: new( & conn, & name, & version) ) ;
@@ -251,3 +235,28 @@ pub fn badge_handler(req: &mut Request) -> IronResult<Response> {
251
235
CacheDirective :: MustRevalidate ] ) ) ;
252
236
Ok ( resp)
253
237
}
238
+
239
+ /// Serves shared web resources used by rustdoc-generated documentation.
240
+ ///
241
+ /// This includes common `css` and `js` files that only change when the compiler is updated, but are
242
+ /// otherwise the same for all crates documented with that compiler. Those have a custom handler to
243
+ /// deduplicate them and save space.
244
+ pub struct SharedResourceHandler ;
245
+
246
+ impl Handler for SharedResourceHandler {
247
+ fn handle ( & self , req : & mut Request ) -> IronResult < Response > {
248
+ let path = req. url . path ( ) ;
249
+ let filename = path. last ( ) . unwrap ( ) ; // unwrap is fine: vector is non-empty
250
+ let suffix = filename. split ( '.' ) . last ( ) . unwrap ( ) ; // unwrap is fine: split always works
251
+ if [ "js" , "css" , "woff" , "svg" ] . contains ( & suffix) {
252
+ let conn = extension ! ( req, Pool ) ;
253
+
254
+ if let Some ( file) = File :: from_path ( conn, filename) {
255
+ return Ok ( file. serve ( ) ) ;
256
+ }
257
+ }
258
+
259
+ // Just always return a 404 here - the main handler will then try the other handlers
260
+ Err ( IronError :: new ( Nope :: ResourceNotFound , status:: NotFound ) )
261
+ }
262
+ }
0 commit comments