@@ -2,7 +2,7 @@ use std::collections::HashMap;
22use std:: net:: SocketAddr ;
33
44use axum:: headers:: HeaderName ;
5- use axum:: http:: { HeaderValue , StatusCode , Uri } ;
5+ use axum:: http:: { HeaderMap , HeaderValue , StatusCode , Uri } ;
66use axum:: response:: { Html , IntoResponse , Response } ;
77use axum:: routing:: { get, get_service} ;
88use axum:: Router ;
@@ -29,6 +29,36 @@ pub struct Options {
2929}
3030
3131pub async fn run_server ( options : Options , output : WasmBindgenOutput ) -> Result < ( ) > {
32+ let app = get_router ( & options, output) ;
33+ let mut address_string = options. address ;
34+ if !address_string. contains ( ":" ) {
35+ address_string +=
36+ & ( ":" . to_owned ( ) + & pick_port:: pick_free_port ( 1334 , 10 ) . unwrap_or ( 1334 ) . to_string ( ) ) ;
37+ }
38+ let addr: SocketAddr = address_string. parse ( ) . expect ( "Couldn't parse address" ) ;
39+
40+ if options. https {
41+ let certificate = rcgen:: generate_simple_self_signed ( [ String :: from ( "localhost" ) ] ) ?;
42+ let config = RustlsConfig :: from_der (
43+ vec ! [ certificate. serialize_der( ) ?] ,
44+ certificate. serialize_private_key_der ( ) ,
45+ )
46+ . await ?;
47+
48+ tracing:: info!( "starting webserver at https://{}" , addr) ;
49+ axum_server_dual_protocol:: bind_dual_protocol ( addr, config)
50+ . set_upgrade ( true )
51+ . serve ( app. into_make_service ( ) )
52+ . await ?;
53+ } else {
54+ tracing:: info!( "starting webserver at http://{}" , addr) ;
55+ axum_server:: bind ( addr) . serve ( app. into_make_service ( ) ) . await ?;
56+ }
57+
58+ Ok ( ( ) )
59+ }
60+
61+ fn get_router ( options : & Options , output : WasmBindgenOutput ) -> Router {
3262 let WasmBindgenOutput { js, compressed_wasm, snippets, local_modules } = output;
3363
3464 let middleware_stack = ServiceBuilder :: new ( )
@@ -53,13 +83,14 @@ pub async fn run_server(options: Options, output: WasmBindgenOutput) -> Result<(
5383 let html = html. replace ( "{{ TITLE }}" , & options. title ) ;
5484
5585 let serve_dir =
56- get_service ( ServeDir :: new ( options. directory ) ) . handle_error ( internal_server_error) ;
86+ get_service ( ServeDir :: new ( options. directory . clone ( ) ) ) . handle_error ( internal_server_error) ;
5787
58- let serve_wasm = || async move {
88+ let serve_wasm = |headers : HeaderMap | async move {
89+ println ! ( " request headers: {:?}" , headers) ;
5990 ( [ ( "content-encoding" , "br" ) ] , WithContentType ( "application/wasm" , compressed_wasm) )
6091 } ;
6192
62- let app = Router :: new ( )
93+ Router :: new ( )
6394 . route ( "/" , get ( move || async { Html ( html) } ) )
6495 . route ( "/api/wasm.js" , get ( || async { WithContentType ( "application/javascript" , js) } ) )
6596 . route ( "/api/wasm.wasm" , get ( serve_wasm) )
@@ -77,34 +108,7 @@ pub async fn run_server(options: Options, output: WasmBindgenOutput) -> Result<(
77108 } ) ,
78109 )
79110 . fallback ( serve_dir)
80- . layer ( middleware_stack) ;
81-
82- let mut address_string = options. address ;
83- if !address_string. contains ( ":" ) {
84- address_string +=
85- & ( ":" . to_owned ( ) + & pick_port:: pick_free_port ( 1334 , 10 ) . unwrap_or ( 1334 ) . to_string ( ) ) ;
86- }
87- let addr: SocketAddr = address_string. parse ( ) . expect ( "Couldn't parse address" ) ;
88-
89- if options. https {
90- let certificate = rcgen:: generate_simple_self_signed ( [ String :: from ( "localhost" ) ] ) ?;
91- let config = RustlsConfig :: from_der (
92- vec ! [ certificate. serialize_der( ) ?] ,
93- certificate. serialize_private_key_der ( ) ,
94- )
95- . await ?;
96-
97- tracing:: info!( "starting webserver at https://{}" , addr) ;
98- axum_server_dual_protocol:: bind_dual_protocol ( addr, config)
99- . set_upgrade ( true )
100- . serve ( app. into_make_service ( ) )
101- . await ?;
102- } else {
103- tracing:: info!( "starting webserver at http://{}" , addr) ;
104- axum_server:: bind ( addr) . serve ( app. into_make_service ( ) ) . await ?;
105- }
106-
107- Ok ( ( ) )
111+ . layer ( middleware_stack)
108112}
109113
110114fn get_snippet_source (
@@ -165,3 +169,75 @@ mod pick_port {
165169 . or_else ( ask_free_tcp_port)
166170 }
167171}
172+
173+ #[ cfg( test) ]
174+ mod tests {
175+ use crate :: server:: get_router;
176+ use crate :: wasm_bindgen;
177+ use crate :: Options ;
178+ use axum:: body:: Bytes ;
179+ use axum:: http:: StatusCode ;
180+ use axum_test_helper:: TestClient ;
181+ use std:: path:: Path ;
182+
183+ /// Headers for requests from 127.0.0.1 and local IP:
184+ ///
185+ /// In this request, br is missing from the "accept-encoding" header
186+ ///
187+ /// request headers: {"host": "192.168.68.107:1334", "connection": "keep-alive",
188+ /// "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36",
189+ /// "accept": "*/*", "referer": "http://192.168.68.107:1334/",
190+ /// "accept-encoding": "gzip, deflate", "accept-language": "en-US,en;q=0.9"}
191+
192+ /// request headers: {"host": "127.0.0.1:1334", "connection": "keep-alive",
193+ /// "sec-ch-ua": "\"Google Chrome\";v=\"107\", \"Chromium\";v=\"107\", \"Not=A?Brand\";v=\"24\"",
194+ /// "sec-ch-ua-mobile": "?0",
195+ /// "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36",
196+ /// "sec-ch-ua-platform": "\"Linux\"", "accept": "*/*", "sec-fetch-site": "same-origin",
197+ /// "sec-fetch-mode": "cors", "sec-fetch-dest": "empty", "referer": "http://127.0.0.1:1334/",
198+ /// "accept-encoding": "gzip, deflate, br", "accept-language": "en-US,en;q=0.9"}
199+
200+ /// To run this test, it is necessary to first create `example-project.wasm` by these commands:
201+ /// cd example-project
202+ /// cargo build
203+ #[ tokio:: test]
204+ async fn test_router ( ) {
205+ let options = Options {
206+ title : "title" . to_string ( ) ,
207+ address : "127.0.0.1:0" . to_string ( ) ,
208+ directory : "." . to_string ( ) ,
209+ https : false ,
210+ no_module : false ,
211+ } ;
212+
213+ let wasm_file =
214+ Path :: new ( "example-project/target/wasm32-unknown-unknown/debug/example-project.wasm" ) ;
215+ let output = wasm_bindgen:: generate ( & options, wasm_file) . unwrap ( ) ;
216+ let router = get_router ( & options, output) ;
217+ println ! ( "{:?}" , & router) ;
218+ let client = TestClient :: new ( router) ;
219+
220+ // Test with br compression requested
221+ let mut res = client
222+ . get ( "/api/wasm.wasm" )
223+ . header ( "accept-encoding" , "gzip, deflate, br" )
224+ . send ( )
225+ . await ;
226+ assert_eq ! ( res. status( ) , StatusCode :: OK ) ;
227+ let result = res. chunk ( ) . await . unwrap ( ) ;
228+ assert_ne ! ( result[ 0 ..3 ] , Bytes :: from( vec![ 0x1f , 0x8b , 0x08 ] ) ) ;
229+
230+ // Test without br compression
231+ // let mut res = client
232+ // .get("/api/wasm.wasm")
233+ // .header("accept-encoding", "gzip, deflate")
234+ // .send()
235+ // .await;
236+ // assert_eq!(res.status(), StatusCode::OK);
237+ // let result = res.chunk().await.unwrap();
238+ // // This is the gzip 3-byte file header
239+ // assert_eq!(result[0..3], Bytes::from(vec![0x1f, 0x8b, 0x08]));
240+
241+ //tokio_test::block_on(server).unwrap();
242+ }
243+ }
0 commit comments