@@ -16,7 +16,8 @@ use rustc_serialize::json::{Json, ToJson};
1616use std:: collections:: BTreeMap ;
1717use iron:: headers:: { Expires , HttpDate , CacheControl , CacheDirective } ;
1818use time;
19-
19+ use iron:: Handler ;
20+ use utils;
2021
2122
2223#[ derive( Debug ) ]
@@ -110,6 +111,10 @@ pub fn rustdoc_redirector_handler(req: &mut Request) -> IronResult<Response> {
110111}
111112
112113
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.
113118pub fn rustdoc_html_server_handler ( req : & mut Request ) -> IronResult < Response > {
114119
115120 let router = extension ! ( req, Router ) ;
@@ -151,34 +156,13 @@ pub fn rustdoc_html_server_handler(req: &mut Request) -> IronResult<Response> {
151156 return Ok ( file. serve ( ) ) ;
152157 }
153158
154- let ( mut in_head, mut in_body) = ( false , false ) ;
155-
156159 let mut content = RustdocPage :: default ( ) ;
157160
158161 let file_content = ctry ! ( String :: from_utf8( file. content) ) ;
159162
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;
182166
183167 content. full = file_content;
184168 let crate_details = cexpect ! ( CrateDetails :: new( & conn, & name, & version) ) ;
@@ -251,3 +235,28 @@ pub fn badge_handler(req: &mut Request) -> IronResult<Response> {
251235 CacheDirective :: MustRevalidate ] ) ) ;
252236 Ok ( resp)
253237}
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