Merge pull request #906 from ethcore/webapps-auth
WebApps HTTP Basic Auth Support
This commit is contained in:
commit
d823fd7685
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -321,7 +321,7 @@ dependencies = [
|
|||||||
"jsonrpc-core 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jsonrpc-core 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"jsonrpc-http-server 4.0.0 (git+https://github.com/tomusdrw/jsonrpc-http-server.git?branch=old-hyper)",
|
"jsonrpc-http-server 4.0.0 (git+https://github.com/tomusdrw/jsonrpc-http-server.git?branch=old-hyper)",
|
||||||
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-status 0.1.4 (git+https://github.com/tomusdrw/parity-status.git)",
|
"parity-status 0.1.5 (git+https://github.com/tomusdrw/parity-status.git)",
|
||||||
"parity-wallet 0.1.0 (git+https://github.com/tomusdrw/parity-wallet.git)",
|
"parity-wallet 0.1.0 (git+https://github.com/tomusdrw/parity-wallet.git)",
|
||||||
"parity-webapp 0.1.0 (git+https://github.com/tomusdrw/parity-webapp.git)",
|
"parity-webapp 0.1.0 (git+https://github.com/tomusdrw/parity-webapp.git)",
|
||||||
]
|
]
|
||||||
@ -727,8 +727,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-status"
|
name = "parity-status"
|
||||||
version = "0.1.4"
|
version = "0.1.5"
|
||||||
source = "git+https://github.com/tomusdrw/parity-status.git#380d13c8aafc3847a731968a6532edb09c78f2cf"
|
source = "git+https://github.com/tomusdrw/parity-status.git#6a075228e9248055a37c55dec41461856f5a9f19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-webapp 0.1.0 (git+https://github.com/tomusdrw/parity-webapp.git)",
|
"parity-webapp 0.1.0 (git+https://github.com/tomusdrw/parity-webapp.git)",
|
||||||
]
|
]
|
||||||
|
@ -136,13 +136,19 @@ API and Console Options:
|
|||||||
interface. APIS is a comma-delimited list of API
|
interface. APIS is a comma-delimited list of API
|
||||||
name. Possible name are web3, eth and net.
|
name. Possible name are web3, eth and net.
|
||||||
[default: web3,eth,net,personal].
|
[default: web3,eth,net,personal].
|
||||||
-w --webapp Enable the web applications server (e.g. status page).
|
-w --webapp Enable the web applications server (e.g.
|
||||||
|
status page).
|
||||||
--webapp-port PORT Specify the port portion of the WebApps server
|
--webapp-port PORT Specify the port portion of the WebApps server
|
||||||
[default: 8080].
|
[default: 8080].
|
||||||
--webapp-interface IP Specify the hostname portion of the WebApps
|
--webapp-interface IP Specify the hostname portion of the WebApps
|
||||||
server, IP should be an interface's IP address, or
|
server, IP should be an interface's IP address, or
|
||||||
all (all interfaces) or local [default: local].
|
all (all interfaces) or local [default: local].
|
||||||
|
--webapp-user USERNAME Specify username for WebApps server. It will be
|
||||||
|
used in HTTP Basic Authentication Scheme.
|
||||||
|
If --webapp-pass is not specified you will be
|
||||||
|
asked for password on startup.
|
||||||
|
--webapp-pass PASSWORD Specify password for WebApps server. Use only in
|
||||||
|
conjunction with --webapp-user.
|
||||||
|
|
||||||
Sealing/Mining Options:
|
Sealing/Mining Options:
|
||||||
--usd-per-tx USD Amount of USD to be paid for a basic transaction
|
--usd-per-tx USD Amount of USD to be paid for a basic transaction
|
||||||
@ -230,6 +236,8 @@ struct Args {
|
|||||||
flag_webapp: bool,
|
flag_webapp: bool,
|
||||||
flag_webapp_port: u16,
|
flag_webapp_port: u16,
|
||||||
flag_webapp_interface: String,
|
flag_webapp_interface: String,
|
||||||
|
flag_webapp_user: Option<String>,
|
||||||
|
flag_webapp_pass: Option<String>,
|
||||||
flag_author: String,
|
flag_author: String,
|
||||||
flag_usd_per_tx: String,
|
flag_usd_per_tx: String,
|
||||||
flag_usd_per_eth: String,
|
flag_usd_per_eth: String,
|
||||||
@ -288,7 +296,7 @@ fn setup_rpc_server(
|
|||||||
miner: Arc<Miner>,
|
miner: Arc<Miner>,
|
||||||
url: &SocketAddr,
|
url: &SocketAddr,
|
||||||
cors_domain: &str,
|
cors_domain: &str,
|
||||||
apis: Vec<&str>
|
apis: Vec<&str>,
|
||||||
) -> RpcServer {
|
) -> RpcServer {
|
||||||
use rpc::v1::*;
|
use rpc::v1::*;
|
||||||
|
|
||||||
@ -321,7 +329,8 @@ fn setup_webapp_server(
|
|||||||
sync: Arc<EthSync>,
|
sync: Arc<EthSync>,
|
||||||
secret_store: Arc<AccountService>,
|
secret_store: Arc<AccountService>,
|
||||||
miner: Arc<Miner>,
|
miner: Arc<Miner>,
|
||||||
url: &str
|
url: &str,
|
||||||
|
auth: Option<(String, String)>,
|
||||||
) -> WebappServer {
|
) -> WebappServer {
|
||||||
use rpc::v1::*;
|
use rpc::v1::*;
|
||||||
|
|
||||||
@ -331,7 +340,14 @@ fn setup_webapp_server(
|
|||||||
server.add_delegate(EthClient::new(&client, &sync, &secret_store, &miner).to_delegate());
|
server.add_delegate(EthClient::new(&client, &sync, &secret_store, &miner).to_delegate());
|
||||||
server.add_delegate(EthFilterClient::new(&client, &miner).to_delegate());
|
server.add_delegate(EthFilterClient::new(&client, &miner).to_delegate());
|
||||||
server.add_delegate(PersonalClient::new(&secret_store).to_delegate());
|
server.add_delegate(PersonalClient::new(&secret_store).to_delegate());
|
||||||
let start_result = server.start_http(url, ::num_cpus::get());
|
let start_result = match auth {
|
||||||
|
None => {
|
||||||
|
server.start_unsecure_http(url, ::num_cpus::get())
|
||||||
|
},
|
||||||
|
Some((username, password)) => {
|
||||||
|
server.start_basic_auth_http(url, ::num_cpus::get(), &username, &password)
|
||||||
|
},
|
||||||
|
};
|
||||||
match start_result {
|
match start_result {
|
||||||
Err(webapp::WebappServerError::IoError(err)) => die_with_io_error(err),
|
Err(webapp::WebappServerError::IoError(err)) => die_with_io_error(err),
|
||||||
Err(e) => die!("{:?}", e),
|
Err(e) => die!("{:?}", e),
|
||||||
@ -351,7 +367,7 @@ fn setup_rpc_server(
|
|||||||
_miner: Arc<Miner>,
|
_miner: Arc<Miner>,
|
||||||
_url: &str,
|
_url: &str,
|
||||||
_cors_domain: &str,
|
_cors_domain: &str,
|
||||||
_apis: Vec<&str>
|
_apis: Vec<&str>,
|
||||||
) -> ! {
|
) -> ! {
|
||||||
die!("Your Parity version has been compiled without JSON-RPC support.")
|
die!("Your Parity version has been compiled without JSON-RPC support.")
|
||||||
}
|
}
|
||||||
@ -365,7 +381,8 @@ fn setup_webapp_server(
|
|||||||
_sync: Arc<EthSync>,
|
_sync: Arc<EthSync>,
|
||||||
_secret_store: Arc<AccountService>,
|
_secret_store: Arc<AccountService>,
|
||||||
_miner: Arc<Miner>,
|
_miner: Arc<Miner>,
|
||||||
_url: &str
|
_url: &str,
|
||||||
|
_auth: Option<(String, String)>,
|
||||||
) -> ! {
|
) -> ! {
|
||||||
die!("Your Parity version has been compiled without WebApps support.")
|
die!("Your Parity version has been compiled without WebApps support.")
|
||||||
}
|
}
|
||||||
@ -683,12 +700,24 @@ impl Configuration {
|
|||||||
},
|
},
|
||||||
self.args.flag_webapp_port
|
self.args.flag_webapp_port
|
||||||
);
|
);
|
||||||
|
let auth = self.args.flag_webapp_user.as_ref().map(|username| {
|
||||||
|
let password = self.args.flag_webapp_pass.as_ref().map_or_else(|| {
|
||||||
|
use rpassword::read_password;
|
||||||
|
println!("Type password for WebApps server (user: {}): ", username);
|
||||||
|
let pass = read_password().unwrap();
|
||||||
|
println!("OK, got it. Starting server...");
|
||||||
|
pass
|
||||||
|
}, |pass| pass.to_owned());
|
||||||
|
(username.to_owned(), password)
|
||||||
|
});
|
||||||
|
|
||||||
Some(setup_webapp_server(
|
Some(setup_webapp_server(
|
||||||
service.client(),
|
service.client(),
|
||||||
sync.clone(),
|
sync.clone(),
|
||||||
account_service.clone(),
|
account_service.clone(),
|
||||||
miner.clone(),
|
miner.clone(),
|
||||||
&url,
|
&url,
|
||||||
|
auth,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -17,7 +17,7 @@ ethcore-rpc = { path = "../rpc" }
|
|||||||
ethcore-util = { path = "../util" }
|
ethcore-util = { path = "../util" }
|
||||||
parity-webapp = { git = "https://github.com/tomusdrw/parity-webapp.git" }
|
parity-webapp = { git = "https://github.com/tomusdrw/parity-webapp.git" }
|
||||||
# List of apps
|
# List of apps
|
||||||
parity-status = { git = "https://github.com/tomusdrw/parity-status.git", version = "0.1.4" }
|
parity-status = { git = "https://github.com/tomusdrw/parity-status.git", version = "0.1.5" }
|
||||||
parity-wallet = { git = "https://github.com/tomusdrw/parity-wallet.git", optional = true }
|
parity-wallet = { git = "https://github.com/tomusdrw/parity-wallet.git", optional = true }
|
||||||
clippy = { version = "0.0.61", optional = true}
|
clippy = { version = "0.0.61", optional = true}
|
||||||
|
|
||||||
|
@ -35,6 +35,8 @@ mod apps;
|
|||||||
mod page;
|
mod page;
|
||||||
mod router;
|
mod router;
|
||||||
|
|
||||||
|
use router::auth::{Authorization, NoAuth, HttpBasicAuth};
|
||||||
|
|
||||||
/// Http server.
|
/// Http server.
|
||||||
pub struct WebappServer {
|
pub struct WebappServer {
|
||||||
handler: Arc<IoHandler>,
|
handler: Arc<IoHandler>,
|
||||||
@ -53,14 +55,25 @@ impl WebappServer {
|
|||||||
self.handler.add_delegate(delegate);
|
self.handler.add_delegate(delegate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start server asynchronously and returns result with `Listening` handle on success or an error.
|
/// Asynchronously start server with no authentication,
|
||||||
pub fn start_http(&self, addr: &str, threads: usize) -> Result<Listening, WebappServerError> {
|
/// return result with `Listening` handle on success or an error.
|
||||||
|
pub fn start_unsecure_http(&self, addr: &str, threads: usize) -> Result<Listening, WebappServerError> {
|
||||||
|
self.start_http(addr, threads, NoAuth)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Asynchronously start server with `HTTP Basic Authentication`,
|
||||||
|
/// return result with `Listening` handle on success or an error.
|
||||||
|
pub fn start_basic_auth_http(&self, addr: &str, threads: usize, username: &str, password: &str) -> Result<Listening, WebappServerError> {
|
||||||
|
self.start_http(addr, threads, HttpBasicAuth::single_user(username, password))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_http<A: Authorization + 'static>(&self, addr: &str, threads: usize, authorization: A) -> Result<Listening, WebappServerError> {
|
||||||
let addr = addr.to_owned();
|
let addr = addr.to_owned();
|
||||||
let handler = self.handler.clone();
|
let handler = self.handler.clone();
|
||||||
|
|
||||||
let cors_domain = jsonrpc_http_server::AccessControlAllowOrigin::Null;
|
let cors_domain = jsonrpc_http_server::AccessControlAllowOrigin::Null;
|
||||||
let rpc = ServerHandler::new(handler, cors_domain);
|
let rpc = ServerHandler::new(handler, cors_domain);
|
||||||
let router = router::Router::new(rpc, apps::main_page(), apps::all_pages());
|
let router = router::Router::new(rpc, apps::main_page(), apps::all_pages(), authorization);
|
||||||
|
|
||||||
try!(hyper::Server::http(addr.as_ref() as &str))
|
try!(hyper::Server::http(addr.as_ref() as &str))
|
||||||
.handle_threads(router, threads)
|
.handle_threads(router, threads)
|
||||||
|
112
webapp/src/router/auth.rs
Normal file
112
webapp/src/router/auth.rs
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Parity is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! HTTP Authorization implementations
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use hyper::{header, server};
|
||||||
|
use hyper::status::StatusCode;
|
||||||
|
|
||||||
|
/// Authorization result
|
||||||
|
pub enum Authorized<'a, 'b> where 'b : 'a {
|
||||||
|
/// Authorization was successful. Request and Response are returned for further processing.
|
||||||
|
Yes(server::Request<'a, 'b>, server::Response<'a>),
|
||||||
|
/// Unsuccessful authorization. Request and Response has been consumed.
|
||||||
|
No,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Authorization interface
|
||||||
|
pub trait Authorization : Send + Sync {
|
||||||
|
/// Handle authorization process and return `Request` and `Response` when authorization is successful.
|
||||||
|
fn handle<'b, 'a>(&'a self, req: server::Request<'a, 'b>, res: server::Response<'a>)-> Authorized<'a, 'b>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// HTTP Basic Authorization handler
|
||||||
|
pub struct HttpBasicAuth {
|
||||||
|
users: HashMap<String, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// No-authorization implementation (authorization disabled)
|
||||||
|
pub struct NoAuth;
|
||||||
|
|
||||||
|
impl Authorization for NoAuth {
|
||||||
|
fn handle<'b, 'a>(&'a self, req: server::Request<'a, 'b>, res: server::Response<'a>)-> Authorized<'a, 'b> {
|
||||||
|
Authorized::Yes(req, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Authorization for HttpBasicAuth {
|
||||||
|
|
||||||
|
fn handle<'b, 'a>(&'a self, req: server::Request<'a, 'b>, res: server::Response<'a>)-> Authorized<'a, 'b> {
|
||||||
|
let auth = self.check_auth(&req);
|
||||||
|
|
||||||
|
match auth {
|
||||||
|
Access::Denied => {
|
||||||
|
self.respond_with_unauthorized(res);
|
||||||
|
Authorized::No
|
||||||
|
},
|
||||||
|
Access::AuthRequired => {
|
||||||
|
self.respond_with_auth_required(res);
|
||||||
|
Authorized::No
|
||||||
|
},
|
||||||
|
Access::Granted => {
|
||||||
|
Authorized::Yes(req, res)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Access {
|
||||||
|
Granted,
|
||||||
|
Denied,
|
||||||
|
AuthRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HttpBasicAuth {
|
||||||
|
/// Creates `HttpBasicAuth` instance with only one user.
|
||||||
|
pub fn single_user(username: &str, password: &str) -> Self {
|
||||||
|
let mut users = HashMap::new();
|
||||||
|
users.insert(username.to_owned(), password.to_owned());
|
||||||
|
HttpBasicAuth {
|
||||||
|
users: users
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_authorized(&self, username: &str, password: &str) -> bool {
|
||||||
|
self.users.get(&username.to_owned()).map_or(false, |pass| pass == password)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_auth(&self, req: &server::Request) -> Access {
|
||||||
|
match req.headers.get::<header::Authorization<header::Basic>>() {
|
||||||
|
Some(&header::Authorization(
|
||||||
|
header::Basic { ref username, password: Some(ref password) }
|
||||||
|
)) if self.is_authorized(username, password) => Access::Granted,
|
||||||
|
Some(_) => Access::Denied,
|
||||||
|
None => Access::AuthRequired,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn respond_with_unauthorized(&self, mut res: server::Response) {
|
||||||
|
*res.status_mut() = StatusCode::Unauthorized;
|
||||||
|
let _ = res.send(b"Unauthorized");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn respond_with_auth_required(&self, mut res: server::Response) {
|
||||||
|
*res.status_mut() = StatusCode::Unauthorized;
|
||||||
|
res.headers_mut().set_raw("WWW-Authenticate", vec![b"Basic realm=\"Parity\"".to_vec()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,45 +15,59 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! Router implementation
|
//! Router implementation
|
||||||
|
//! Processes request handling authorization and dispatching it to proper application.
|
||||||
|
|
||||||
|
mod api;
|
||||||
|
pub mod auth;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use hyper;
|
use hyper;
|
||||||
|
use hyper::{server, uri, header};
|
||||||
use page::Page;
|
use page::Page;
|
||||||
use apps::Pages;
|
use apps::Pages;
|
||||||
use iron::request::Url;
|
use iron::request::Url;
|
||||||
use jsonrpc_http_server::ServerHandler;
|
use jsonrpc_http_server::ServerHandler;
|
||||||
|
use self::auth::{Authorization, Authorized};
|
||||||
|
|
||||||
mod api;
|
pub struct Router<A: Authorization> {
|
||||||
|
authorization: A,
|
||||||
pub struct Router {
|
|
||||||
rpc: ServerHandler,
|
rpc: ServerHandler,
|
||||||
api: api::RestApi,
|
api: api::RestApi,
|
||||||
main_page: Box<Page>,
|
main_page: Box<Page>,
|
||||||
pages: Arc<Pages>,
|
pages: Arc<Pages>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl hyper::server::Handler for Router {
|
impl<A: Authorization> server::Handler for Router<A> {
|
||||||
fn handle<'b, 'a>(&'a self, req: hyper::server::Request<'a, 'b>, res: hyper::server::Response<'a>) {
|
fn handle<'b, 'a>(&'a self, req: server::Request<'a, 'b>, res: server::Response<'a>) {
|
||||||
let (path, req) = Router::extract_request_path(req);
|
let auth = self.authorization.handle(req, res);
|
||||||
match path {
|
|
||||||
Some(ref url) if self.pages.contains_key(url) => {
|
if let Authorized::Yes(req, res) = auth {
|
||||||
self.pages.get(url).unwrap().handle(req, res);
|
let (path, req) = self.extract_request_path(req);
|
||||||
},
|
match path {
|
||||||
Some(ref url) if url == "api" => {
|
Some(ref url) if self.pages.contains_key(url) => {
|
||||||
self.api.handle(req, res);
|
self.pages.get(url).unwrap().handle(req, res);
|
||||||
},
|
},
|
||||||
_ if req.method == hyper::method::Method::Post => {
|
Some(ref url) if url == "api" => {
|
||||||
self.rpc.handle(req, res)
|
self.api.handle(req, res);
|
||||||
},
|
},
|
||||||
_ => self.main_page.handle(req, res),
|
_ if req.method == hyper::method::Method::Post => {
|
||||||
|
self.rpc.handle(req, res)
|
||||||
|
},
|
||||||
|
_ => self.main_page.handle(req, res),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Router {
|
impl<A: Authorization> Router<A> {
|
||||||
pub fn new(rpc: ServerHandler, main_page: Box<Page>, pages: Pages) -> Self {
|
pub fn new(
|
||||||
|
rpc: ServerHandler,
|
||||||
|
main_page: Box<Page>,
|
||||||
|
pages: Pages,
|
||||||
|
authorization: A) -> Self {
|
||||||
let pages = Arc::new(pages);
|
let pages = Arc::new(pages);
|
||||||
Router {
|
Router {
|
||||||
|
authorization: authorization,
|
||||||
rpc: rpc,
|
rpc: rpc,
|
||||||
api: api::RestApi { pages: pages.clone() },
|
api: api::RestApi { pages: pages.clone() },
|
||||||
main_page: main_page,
|
main_page: main_page,
|
||||||
@ -61,17 +75,17 @@ impl Router {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_url(req: &hyper::server::Request) -> Option<Url> {
|
fn extract_url(&self, req: &server::Request) -> Option<Url> {
|
||||||
match req.uri {
|
match req.uri {
|
||||||
hyper::uri::RequestUri::AbsoluteUri(ref url) => {
|
uri::RequestUri::AbsoluteUri(ref url) => {
|
||||||
match Url::from_generic_url(url.clone()) {
|
match Url::from_generic_url(url.clone()) {
|
||||||
Ok(url) => Some(url),
|
Ok(url) => Some(url),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
hyper::uri::RequestUri::AbsolutePath(ref path) => {
|
uri::RequestUri::AbsolutePath(ref path) => {
|
||||||
// Attempt to prepend the Host header (mandatory in HTTP/1.1)
|
// Attempt to prepend the Host header (mandatory in HTTP/1.1)
|
||||||
let url_string = match req.headers.get::<hyper::header::Host>() {
|
let url_string = match req.headers.get::<header::Host>() {
|
||||||
Some(ref host) => {
|
Some(ref host) => {
|
||||||
format!("http://{}:{}{}", host.hostname, host.port.unwrap_or(80), path)
|
format!("http://{}:{}{}", host.hostname, host.port.unwrap_or(80), path)
|
||||||
},
|
},
|
||||||
@ -87,18 +101,18 @@ impl Router {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_request_path<'a, 'b>(mut req: hyper::server::Request<'a, 'b>) -> (Option<String>, hyper::server::Request<'a, 'b>) {
|
fn extract_request_path<'a, 'b>(&self, mut req: server::Request<'a, 'b>) -> (Option<String>, server::Request<'a, 'b>) {
|
||||||
let url = Router::extract_url(&req);
|
let url = self.extract_url(&req);
|
||||||
match url {
|
match url {
|
||||||
Some(ref url) if url.path.len() > 1 => {
|
Some(ref url) if url.path.len() > 1 => {
|
||||||
let part = url.path[0].clone();
|
let part = url.path[0].clone();
|
||||||
let url = url.path[1..].join("/");
|
let url = url.path[1..].join("/");
|
||||||
req.uri = hyper::uri::RequestUri::AbsolutePath(url);
|
req.uri = uri::RequestUri::AbsolutePath(url);
|
||||||
(Some(part), req)
|
(Some(part), req)
|
||||||
},
|
},
|
||||||
Some(url) => {
|
Some(url) => {
|
||||||
let url = url.path.join("/");
|
let url = url.path.join("/");
|
||||||
req.uri = hyper::uri::RequestUri::AbsolutePath(url);
|
req.uri = uri::RequestUri::AbsolutePath(url);
|
||||||
(None, req)
|
(None, req)
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
|
Loading…
Reference in New Issue
Block a user