From 968c81e2678ee301b5f685181bac5edec7d312b2 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sun, 2 Sep 2018 08:14:54 -0700 Subject: [PATCH] Handling scoped paths without leading slashes #460 --- CHANGES.md | 4 ++- src/scope.rs | 84 +++++++++++++++++++++++++++++++++++--------- tests/test_server.rs | 3 +- 3 files changed, 71 insertions(+), 20 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index b8ab0f879..dd6cdcd20 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,6 @@ # Changes -## [0.7.5] - 2018-09-xx +## [0.7.5] - 2018-09-02 ### Added @@ -13,6 +13,8 @@ * Handle socket read disconnect +* Handling scoped paths without leading slashes #460 + ## [0.7.4] - 2018-08-23 diff --git a/src/scope.rs b/src/scope.rs index 83e43f43a..a1fd907a1 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -183,7 +183,7 @@ impl Scope { where F: FnOnce(Scope) -> Scope, { - let rdef = ResourceDef::prefix(&path); + let rdef = ResourceDef::prefix(&insert_slash(path)); let scope = Scope { rdef: rdef.clone(), filters: Vec::new(), @@ -230,9 +230,11 @@ impl Scope { R: Responder + 'static, T: FromRequest + 'static, { - Rc::get_mut(&mut self.router) - .unwrap() - .register_route(path, method, f); + Rc::get_mut(&mut self.router).unwrap().register_route( + &insert_slash(path), + method, + f, + ); self } @@ -264,7 +266,7 @@ impl Scope { F: FnOnce(&mut Resource) -> R + 'static, { // add resource - let mut resource = Resource::new(ResourceDef::new(path)); + let mut resource = Resource::new(ResourceDef::new(&insert_slash(path))); f(&mut resource); Rc::get_mut(&mut self.router) @@ -311,19 +313,17 @@ impl Scope { /// } /// ``` pub fn handler>(mut self, path: &str, handler: H) -> Scope { - { - let mut path = path.trim().trim_right_matches('/').to_owned(); - if !path.is_empty() && !path.starts_with('/') { - path.insert(0, '/') - } - if path.len() > 1 && path.ends_with('/') { - path.pop(); - } - - Rc::get_mut(&mut self.router) - .expect("Multiple copies of scope router") - .register_handler(&path, Box::new(WrapHandler::new(handler)), None); + let mut path = path.trim().trim_right_matches('/').to_owned(); + if !path.is_empty() && !path.starts_with('/') { + path.insert(0, '/') } + if path.len() > 1 && path.ends_with('/') { + path.pop(); + } + + Rc::get_mut(&mut self.router) + .expect("Multiple copies of scope router") + .register_handler(&path, Box::new(WrapHandler::new(handler)), None); self } @@ -342,6 +342,14 @@ impl Scope { } } +fn insert_slash(path: &str) -> String { + let mut path = path.to_owned(); + if !path.is_empty() && !path.starts_with('/') { + path.insert(0, '/'); + }; + path +} + impl RouteHandler for Scope { fn handle(&self, req: &HttpRequest) -> AsyncResult { let tail = req.match_info().tail as usize; @@ -844,6 +852,34 @@ mod tests { assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); } + #[test] + fn test_scope_route_without_leading_slash() { + let app = App::new() + .scope("app", |scope| { + scope + .route("path1", Method::GET, |_: HttpRequest<_>| HttpResponse::Ok()) + .route("path1", Method::DELETE, |_: HttpRequest<_>| { + HttpResponse::Ok() + }) + }).finish(); + + let req = TestRequest::with_uri("/app/path1").request(); + let resp = app.run(req); + assert_eq!(resp.as_msg().status(), StatusCode::OK); + + let req = TestRequest::with_uri("/app/path1") + .method(Method::DELETE) + .request(); + let resp = app.run(req); + assert_eq!(resp.as_msg().status(), StatusCode::OK); + + let req = TestRequest::with_uri("/app/path1") + .method(Method::POST) + .request(); + let resp = app.run(req); + assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); + } + #[test] fn test_scope_filter() { let app = App::new() @@ -1013,6 +1049,20 @@ mod tests { assert_eq!(resp.as_msg().status(), StatusCode::CREATED); } + #[test] + fn test_nested_scope_no_slash() { + let app = App::new() + .scope("/app", |scope| { + scope.nested("t1", |scope| { + scope.resource("/path1", |r| r.f(|_| HttpResponse::Created())) + }) + }).finish(); + + let req = TestRequest::with_uri("/app/t1/path1").request(); + let resp = app.run(req); + assert_eq!(resp.as_msg().status(), StatusCode::CREATED); + } + #[test] fn test_nested_scope_root() { let app = App::new() diff --git a/tests/test_server.rs b/tests/test_server.rs index 8235be6b6..97161a30f 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -948,8 +948,7 @@ fn test_default_404_handler_response() { tokio::io::write_all(sock, "HEAD / HTTP/1.1\r\nHost: localhost\r\n\r\n") .and_then(|(sock, _)| tokio::io::read_exact(sock, &mut buf)) .and_then(|(_, buf)| Ok(buf)) - }) - .map_err(|e| panic!("{:?}", e)); + }).map_err(|e| panic!("{:?}", e)); let response = srv.execute(request).unwrap(); let rep = String::from_utf8_lossy(&response[..]); assert!(rep.contains("HTTP/1.1 404 Not Found"));