@@ -92,16 +92,34 @@ fn set_serve_error(msg: &'static str, e: errors::Error) {
92
92
}
93
93
}
94
94
95
- async fn handle_request ( req : Request < Body > , mut root : PathBuf ) -> Result < Response < Body > > {
95
+ async fn handle_request (
96
+ req : Request < Body > ,
97
+ mut root : PathBuf ,
98
+ base_path : String ,
99
+ ) -> Result < Response < Body > > {
100
+ let path_str = req. uri ( ) . path ( ) ;
101
+ if !path_str. starts_with ( & base_path) {
102
+ return Ok ( not_found ( ) ) ;
103
+ }
104
+
105
+ let trimmed_path = & path_str[ base_path. len ( ) - 1 ..] ;
106
+
96
107
let original_root = root. clone ( ) ;
97
108
let mut path = RelativePathBuf :: new ( ) ;
98
109
// https://zola.discourse.group/t/percent-encoding-for-slugs/736
99
- let decoded = match percent_encoding:: percent_decode_str ( req . uri ( ) . path ( ) ) . decode_utf8 ( ) {
110
+ let decoded = match percent_encoding:: percent_decode_str ( trimmed_path ) . decode_utf8 ( ) {
100
111
Ok ( d) => d,
101
112
Err ( _) => return Ok ( not_found ( ) ) ,
102
113
} ;
103
114
104
- for c in decoded. split ( '/' ) {
115
+ let decoded_path = if base_path != "/" && decoded. starts_with ( & base_path) {
116
+ // Remove the base_path from the request path before processing
117
+ decoded[ base_path. len ( ) ..] . to_string ( )
118
+ } else {
119
+ decoded. to_string ( )
120
+ } ;
121
+
122
+ for c in decoded_path. split ( '/' ) {
105
123
path. push ( c) ;
106
124
}
107
125
@@ -318,6 +336,39 @@ fn rebuild_done_handling(broadcaster: &Sender, res: Result<()>, reload_path: &st
318
336
}
319
337
}
320
338
339
+ fn construct_url ( base_url : & str , no_port_append : bool , interface_port : u16 ) -> String {
340
+ if base_url == "/" {
341
+ return String :: from ( "/" ) ;
342
+ }
343
+
344
+ let ( protocol, stripped_url) = match base_url {
345
+ url if url. starts_with ( "http://" ) => ( "http://" , & url[ 7 ..] ) ,
346
+ url if url. starts_with ( "https://" ) => ( "https://" , & url[ 8 ..] ) ,
347
+ url => ( "http://" , url) ,
348
+ } ;
349
+
350
+ let ( domain, path) = {
351
+ let parts: Vec < & str > = stripped_url. splitn ( 2 , '/' ) . collect ( ) ;
352
+ if parts. len ( ) > 1 {
353
+ ( parts[ 0 ] , format ! ( "/{}" , parts[ 1 ] ) )
354
+ } else {
355
+ ( parts[ 0 ] , String :: new ( ) )
356
+ }
357
+ } ;
358
+
359
+ let full_address = if no_port_append {
360
+ format ! ( "{}{}{}" , protocol, domain, path)
361
+ } else {
362
+ format ! ( "{}{}:{}{}" , protocol, domain, interface_port, path)
363
+ } ;
364
+
365
+ if full_address. ends_with ( '/' ) {
366
+ full_address
367
+ } else {
368
+ format ! ( "{}/" , full_address)
369
+ }
370
+ }
371
+
321
372
#[ allow( clippy:: too_many_arguments) ]
322
373
fn create_new_site (
323
374
root_dir : & Path ,
@@ -330,7 +381,7 @@ fn create_new_site(
330
381
include_drafts : bool ,
331
382
mut no_port_append : bool ,
332
383
ws_port : Option < u16 > ,
333
- ) -> Result < ( Site , SocketAddr ) > {
384
+ ) -> Result < ( Site , SocketAddr , String ) > {
334
385
SITE_CONTENT . write ( ) . unwrap ( ) . clear ( ) ;
335
386
336
387
let mut site = Site :: new ( root_dir, config_file) ?;
@@ -345,24 +396,10 @@ fn create_new_site(
345
396
|u| u. to_string ( ) ,
346
397
) ;
347
398
348
- let base_url = if base_url == "/" {
349
- String :: from ( "/" )
350
- } else {
351
- let base_address = if no_port_append {
352
- base_url. to_string ( )
353
- } else {
354
- format ! ( "{}:{}" , base_url, interface_port)
355
- } ;
356
-
357
- if site. config . base_url . ends_with ( '/' ) {
358
- format ! ( "http://{}/" , base_address)
359
- } else {
360
- format ! ( "http://{}" , base_address)
361
- }
362
- } ;
399
+ let constructed_base_url = construct_url ( & base_url, no_port_append, interface_port) ;
363
400
364
401
site. enable_serve_mode ( ) ;
365
- site. set_base_url ( base_url ) ;
402
+ site. set_base_url ( constructed_base_url . clone ( ) ) ;
366
403
if let Some ( output_dir) = output_dir {
367
404
if !force && output_dir. exists ( ) {
368
405
return Err ( Error :: msg ( format ! (
@@ -384,7 +421,7 @@ fn create_new_site(
384
421
messages:: notify_site_size ( & site) ;
385
422
messages:: warn_about_ignored_pages ( & site) ;
386
423
site. build ( ) ?;
387
- Ok ( ( site, address) )
424
+ Ok ( ( site, address, constructed_base_url ) )
388
425
}
389
426
390
427
#[ allow( clippy:: too_many_arguments) ]
@@ -403,7 +440,7 @@ pub fn serve(
403
440
utc_offset : UtcOffset ,
404
441
) -> Result < ( ) > {
405
442
let start = Instant :: now ( ) ;
406
- let ( mut site, bind_address) = create_new_site (
443
+ let ( mut site, bind_address, constructed_base_url ) = create_new_site (
407
444
root_dir,
408
445
interface,
409
446
interface_port,
@@ -415,6 +452,11 @@ pub fn serve(
415
452
no_port_append,
416
453
None ,
417
454
) ?;
455
+ let base_path = match constructed_base_url. splitn ( 4 , '/' ) . nth ( 3 ) {
456
+ Some ( path) => format ! ( "/{}" , path) ,
457
+ None => "/" . to_string ( ) ,
458
+ } ;
459
+
418
460
messages:: report_elapsed_time ( start) ;
419
461
420
462
// Stop right there if we can't bind to the address
@@ -479,19 +521,27 @@ pub fn serve(
479
521
rt. block_on ( async {
480
522
let make_service = make_service_fn ( move |_| {
481
523
let static_root = static_root. clone ( ) ;
524
+ let base_path = base_path. clone ( ) ;
482
525
483
526
async {
484
527
Ok :: < _ , hyper:: Error > ( service_fn ( move |req| {
485
- response_error_injector ( handle_request ( req, static_root. clone ( ) ) )
528
+ response_error_injector ( handle_request (
529
+ req,
530
+ static_root. clone ( ) ,
531
+ base_path. clone ( ) ,
532
+ ) )
486
533
} ) )
487
534
}
488
535
} ) ;
489
536
490
537
let server = Server :: bind ( & bind_address) . serve ( make_service) ;
491
538
492
- println ! ( "Web server is available at http://{}\n " , bind_address) ;
539
+ println ! (
540
+ "Web server is available at {} (bound to {})\n " ,
541
+ & constructed_base_url, & bind_address
542
+ ) ;
493
543
if open {
494
- if let Err ( err) = open:: that ( format ! ( "http:// {}" , bind_address ) ) {
544
+ if let Err ( err) = open:: that ( format ! ( "{}" , & constructed_base_url ) ) {
495
545
eprintln ! ( "Failed to open URL in your browser: {}" , err) ;
496
546
}
497
547
}
@@ -618,7 +668,7 @@ pub fn serve(
618
668
no_port_append,
619
669
ws_port,
620
670
) {
621
- Ok ( ( s, _) ) => {
671
+ Ok ( ( s, _, _ ) ) => {
622
672
clear_serve_error ( ) ;
623
673
rebuild_done_handling ( & broadcaster, Ok ( ( ) ) , "/x.js" ) ;
624
674
@@ -801,7 +851,7 @@ fn is_folder_empty(dir: &Path) -> bool {
801
851
mod tests {
802
852
use std:: path:: { Path , PathBuf } ;
803
853
804
- use super :: { detect_change_kind, is_temp_file, ChangeKind } ;
854
+ use super :: { construct_url , detect_change_kind, is_temp_file, ChangeKind } ;
805
855
806
856
#[ test]
807
857
fn can_recognize_temp_files ( ) {
@@ -893,4 +943,40 @@ mod tests {
893
943
let config_filename = Path :: new ( "config.toml" ) ;
894
944
assert_eq ! ( expected, detect_change_kind( pwd, path, config_filename) ) ;
895
945
}
946
+
947
+ #[ test]
948
+ fn test_construct_url_base_url_is_slash ( ) {
949
+ let result = construct_url ( "/" , false , 8080 ) ;
950
+ assert_eq ! ( result, "/" ) ;
951
+ }
952
+
953
+ #[ test]
954
+ fn test_construct_url_http_protocol ( ) {
955
+ let result = construct_url ( "http://example.com" , false , 8080 ) ;
956
+ assert_eq ! ( result, "http://example.com:8080/" ) ;
957
+ }
958
+
959
+ #[ test]
960
+ fn test_construct_url_https_protocol ( ) {
961
+ let result = construct_url ( "https://example.com" , false , 8080 ) ;
962
+ assert_eq ! ( result, "https://example.com:8080/" ) ;
963
+ }
964
+
965
+ #[ test]
966
+ fn test_construct_url_no_protocol ( ) {
967
+ let result = construct_url ( "example.com" , false , 8080 ) ;
968
+ assert_eq ! ( result, "http://example.com:8080/" ) ;
969
+ }
970
+
971
+ #[ test]
972
+ fn test_construct_url_no_port_append ( ) {
973
+ let result = construct_url ( "https://example.com" , true , 8080 ) ;
974
+ assert_eq ! ( result, "https://example.com/" ) ;
975
+ }
976
+
977
+ #[ test]
978
+ fn test_construct_url_trailing_slash ( ) {
979
+ let result = construct_url ( "http://example.com/" , false , 8080 ) ;
980
+ assert_eq ! ( result, "http://example.com:8080/" ) ;
981
+ }
896
982
}
0 commit comments