@@ -14,8 +14,11 @@ use crate::{
14
14
} ;
15
15
use anyhow:: { anyhow, Context } ;
16
16
use iron:: {
17
- modifiers:: Redirect , status, url:: percent_encoding:: percent_decode, Handler , IronResult ,
18
- Request , Response , Url ,
17
+ headers:: { Link , LinkValue , RelationType } ,
18
+ modifiers:: Redirect ,
19
+ status,
20
+ url:: percent_encoding:: percent_decode,
21
+ Handler , IronResult , Request , Response , Url ,
19
22
} ;
20
23
use lol_html:: errors:: RewritingError ;
21
24
use once_cell:: sync:: Lazy ;
@@ -243,6 +246,8 @@ impl RustdocPage {
243
246
. expect ( "missing Metrics from the request extensions" ) ;
244
247
245
248
let is_latest_url = self . is_latest_url ;
249
+ let canonical_url = self . canonical_url . clone ( ) ;
250
+
246
251
// Build the page of documentation
247
252
let ctx = ctry ! ( req, tera:: Context :: from_serialize( self ) ) ;
248
253
let config = extension ! ( req, Config ) ;
@@ -264,6 +269,11 @@ impl RustdocPage {
264
269
265
270
let mut response = Response :: with ( ( Status :: Ok , html) ) ;
266
271
response. headers . set ( ContentType :: html ( ) ) ;
272
+ let link_value = LinkValue :: new ( canonical_url)
273
+ . push_rel ( RelationType :: ExtRelType ( "canonical" . to_string ( ) ) ) ;
274
+
275
+ response. headers . set ( Link :: new ( vec ! [ link_value] ) ) ;
276
+
267
277
response. extensions . insert :: < CachePolicy > ( if is_latest_url {
268
278
CachePolicy :: ForeverInCdn
269
279
} else {
@@ -2310,31 +2320,45 @@ mod test {
2310
2320
assert ! ( web
2311
2321
. get( "/dummy-dash/0.1.0/dummy_dash/" )
2312
2322
. send( ) ?
2313
- . text( ) ?
2323
+ . headers( )
2324
+ . get( "link" )
2325
+ . unwrap( )
2326
+ . to_str( )
2327
+ . unwrap( )
2314
2328
. contains( "rel=\" canonical\" " ) , ) ;
2315
2329
2316
2330
assert ! ( web
2317
2331
. get( "/dummy-docs/0.1.0/dummy_docs/" )
2318
2332
. send( ) ?
2319
- . text( ) ?
2320
- . contains(
2321
- "<link rel=\" canonical\" href=\" https://docs.rs/dummy-docs/latest/dummy_docs/\" />"
2322
- ) , ) ;
2333
+ . headers( )
2334
+ . get( "link" )
2335
+ . unwrap( )
2336
+ . to_str( )
2337
+ . unwrap( )
2338
+ . contains( "<https://docs.rs/dummy-docs/latest/dummy_docs/>; rel=\" canonical\" " ) , ) ;
2323
2339
2324
- assert ! (
2325
- web
2326
- . get( "/dummy-nodocs/0.1.0/dummy_nodocs/" )
2327
- . send( ) ?
2328
- . text( ) ?
2329
- . contains( "<link rel=\" canonical\" href=\" https://docs.rs/dummy-nodocs/latest/dummy_nodocs/\" />" ) ,
2330
- ) ;
2340
+ assert ! ( web
2341
+ . get( "/dummy-nodocs/0.1.0/dummy_nodocs/" )
2342
+ . send( ) ?
2343
+ . headers( )
2344
+ . get( "link" )
2345
+ . unwrap( )
2346
+ . to_str( )
2347
+ . unwrap( )
2348
+ . contains(
2349
+ "<https://docs.rs/dummy-nodocs/latest/dummy_nodocs/>; rel=\" canonical\" "
2350
+ ) , ) ;
2331
2351
2332
2352
assert ! (
2333
2353
web
2334
2354
. get( "/dummy-nodocs/0.1.0/dummy_nodocs/struct.Foo.html" )
2335
2355
. send( ) ?
2336
- . text( ) ?
2337
- . contains( "<link rel=\" canonical\" href=\" https://docs.rs/dummy-nodocs/latest/dummy_nodocs/struct.Foo.html\" />" ) ,
2356
+ . headers( )
2357
+ . get( "link" )
2358
+ . unwrap( )
2359
+ . to_str( )
2360
+ . unwrap( )
2361
+ . contains( "<https://docs.rs/dummy-nodocs/latest/dummy_nodocs/struct.Foo.html>; rel=\" canonical\" " ) ,
2338
2362
) ;
2339
2363
Ok ( ( ) )
2340
2364
} )
0 commit comments