2017-01-25 18:51:41 +01:00
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
2016-12-22 18:26:39 +01:00
// 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/>.
//! Serving web-based content (proxying)
2016-12-27 11:15:02 +01:00
use std ::sync ::Arc ;
2016-12-22 18:26:39 +01:00
use fetch ::{ self , Fetch } ;
2016-12-27 11:15:02 +01:00
use parity_reactor ::Remote ;
2017-02-04 09:52:14 +01:00
use base32 ;
2016-12-22 18:26:39 +01:00
use hyper ::{ self , server , net , Next , Encoder , Decoder } ;
use hyper ::status ::StatusCode ;
2016-12-27 11:15:02 +01:00
use apps ;
use endpoint ::{ Endpoint , Handler , EndpointPath } ;
use handlers ::{
ContentFetcherHandler , ContentHandler , ContentValidator , ValidatorResponse ,
2017-02-04 09:52:14 +01:00
StreamingHandler , extract_url ,
2016-12-27 11:15:02 +01:00
} ;
use url ::Url ;
use WebProxyTokens ;
pub type Embeddable = Option < ( String , u16 ) > ;
2016-12-22 18:26:39 +01:00
pub struct Web < F > {
2016-12-27 11:15:02 +01:00
embeddable_on : Embeddable ,
web_proxy_tokens : Arc < WebProxyTokens > ,
2016-12-22 18:26:39 +01:00
remote : Remote ,
fetch : F ,
}
impl < F : Fetch > Web < F > {
2016-12-27 11:15:02 +01:00
pub fn boxed ( embeddable_on : Embeddable , web_proxy_tokens : Arc < WebProxyTokens > , remote : Remote , fetch : F ) -> Box < Endpoint > {
2016-12-22 18:26:39 +01:00
Box ::new ( Web {
2016-12-27 11:15:02 +01:00
embeddable_on : embeddable_on ,
web_proxy_tokens : web_proxy_tokens ,
2016-12-22 18:26:39 +01:00
remote : remote ,
fetch : fetch ,
} )
}
}
impl < F : Fetch > Endpoint for Web < F > {
fn to_async_handler ( & self , path : EndpointPath , control : hyper ::Control ) -> Box < Handler > {
Box ::new ( WebHandler {
control : control ,
state : State ::Initial ,
path : path ,
remote : self . remote . clone ( ) ,
fetch : self . fetch . clone ( ) ,
2016-12-27 11:15:02 +01:00
web_proxy_tokens : self . web_proxy_tokens . clone ( ) ,
embeddable_on : self . embeddable_on . clone ( ) ,
2016-12-22 18:26:39 +01:00
} )
}
}
2016-12-27 11:15:02 +01:00
struct WebInstaller {
embeddable_on : Embeddable ,
2017-01-16 10:49:27 +01:00
referer : String ,
2016-12-27 11:15:02 +01:00
}
2016-12-22 18:26:39 +01:00
impl ContentValidator for WebInstaller {
type Error = String ;
2016-12-27 11:15:02 +01:00
fn validate_and_install ( & self , response : fetch ::Response ) -> Result < ValidatorResponse , String > {
let status = StatusCode ::from_u16 ( response . status ( ) . to_u16 ( ) ) ;
let is_html = response . is_html ( ) ;
let mime = response . content_type ( ) . unwrap_or ( mime! ( Text / Html ) ) ;
let mut handler = StreamingHandler ::new (
response ,
status ,
mime ,
self . embeddable_on . clone ( ) ,
) ;
if is_html {
2017-01-16 10:49:27 +01:00
handler . set_initial_content ( & format! (
2017-02-04 09:52:14 +01:00
r # "<script src="/{}/inject.js"></script><script>history.replaceState({{}}, "", "/?{}{}/{}")</script>"# ,
2017-01-16 10:49:27 +01:00
apps ::UTILS_PATH ,
apps ::URL_REFERER ,
2017-02-04 09:52:14 +01:00
apps ::WEB_PATH ,
2017-01-16 10:49:27 +01:00
& self . referer ,
) ) ;
2016-12-27 11:15:02 +01:00
}
Ok ( ValidatorResponse ::Streaming ( handler ) )
2016-12-22 18:26:39 +01:00
}
}
enum State < F : Fetch > {
Initial ,
Error ( ContentHandler ) ,
Fetching ( ContentFetcherHandler < WebInstaller , F > ) ,
}
struct WebHandler < F : Fetch > {
control : hyper ::Control ,
state : State < F > ,
path : EndpointPath ,
remote : Remote ,
fetch : F ,
2016-12-27 11:15:02 +01:00
web_proxy_tokens : Arc < WebProxyTokens > ,
embeddable_on : Embeddable ,
2016-12-22 18:26:39 +01:00
}
impl < F : Fetch > WebHandler < F > {
2017-02-04 09:52:14 +01:00
fn extract_target_url ( & self , url : Option < Url > ) -> Result < String , State < F > > {
let token_and_url = self . path . app_params . get ( 0 )
. map ( | encoded | encoded . replace ( '.' , " " ) )
. and_then ( | encoded | base32 ::decode ( base32 ::Alphabet ::Crockford , & encoded . to_uppercase ( ) ) )
. and_then ( | data | String ::from_utf8 ( data ) . ok ( ) )
. ok_or_else ( | | State ::Error ( ContentHandler ::error (
StatusCode ::BadRequest ,
" Invalid parameter " ,
" Couldn't parse given parameter: " ,
self . path . app_params . get ( 0 ) . map ( String ::as_str ) ,
self . embeddable_on . clone ( )
) ) ) ? ;
let mut token_it = token_and_url . split ( '+' ) ;
let token = token_it . next ( ) ;
let target_url = token_it . next ( ) ;
2016-12-22 18:26:39 +01:00
2016-12-27 11:15:02 +01:00
// Check if token supplied in URL is correct.
2017-02-04 09:52:14 +01:00
match token {
Some ( token ) if self . web_proxy_tokens . is_web_proxy_token_valid ( token ) = > { } ,
2016-12-27 11:15:02 +01:00
_ = > {
return Err ( State ::Error ( ContentHandler ::error (
StatusCode ::BadRequest , " Invalid Access Token " , " Invalid or old web proxy access token supplied. " , Some ( " Try refreshing the page. " ) , self . embeddable_on . clone ( )
) ) ) ;
}
}
2016-12-22 18:26:39 +01:00
// Validate protocol
2017-02-04 09:52:14 +01:00
let mut target_url = match target_url {
Some ( url ) if url . starts_with ( " http:// " ) | | url . starts_with ( " https:// " ) = > url . to_owned ( ) ,
2016-12-22 18:26:39 +01:00
_ = > {
2016-12-27 11:15:02 +01:00
return Err ( State ::Error ( ContentHandler ::error (
StatusCode ::BadRequest , " Invalid Protocol " , " Invalid protocol used. " , None , self . embeddable_on . clone ( )
) ) ) ;
2016-12-22 18:26:39 +01:00
}
} ;
2017-02-04 09:52:14 +01:00
if ! target_url . ends_with ( " / " ) {
target_url = format! ( " {} / " , target_url ) ;
2016-12-22 18:26:39 +01:00
}
2017-02-04 09:52:14 +01:00
// TODO [ToDr] Should just use `path.app_params`
let ( path , query ) = match ( & url , self . path . using_dapps_domains ) {
( & Some ( ref url ) , true ) = > ( & url . path [ .. ] , & url . query ) ,
( & Some ( ref url ) , false ) = > ( & url . path [ 2 .. ] , & url . query ) ,
_ = > {
return Err ( State ::Error ( ContentHandler ::error (
StatusCode ::BadRequest , " Invalid URL " , " Couldn't parse URL " , None , self . embeddable_on . clone ( )
) ) ) ;
}
} ;
let query = match * query {
Some ( ref query ) = > format! ( " ? {} " , query ) ,
2016-12-27 11:15:02 +01:00
None = > " " . into ( ) ,
} ;
2017-02-04 09:52:14 +01:00
Ok ( format! ( " {} {} {} " , target_url , path . join ( " / " ) , query ) )
2016-12-22 18:26:39 +01:00
}
}
impl < F : Fetch > server ::Handler < net ::HttpStream > for WebHandler < F > {
fn on_request ( & mut self , request : server ::Request < net ::HttpStream > ) -> Next {
let url = extract_url ( & request ) ;
// First extract the URL (reject invalid URLs)
2017-02-04 09:52:14 +01:00
let target_url = match self . extract_target_url ( url ) {
2016-12-22 18:26:39 +01:00
Ok ( url ) = > url ,
Err ( error ) = > {
self . state = error ;
return Next ::write ( ) ;
}
} ;
2016-12-27 11:15:02 +01:00
let mut handler = ContentFetcherHandler ::new (
2016-12-22 18:26:39 +01:00
target_url ,
self . path . clone ( ) ,
self . control . clone ( ) ,
2016-12-27 11:15:02 +01:00
WebInstaller {
embeddable_on : self . embeddable_on . clone ( ) ,
2017-02-04 09:52:14 +01:00
referer : self . path . app_params . get ( 0 )
. expect ( " `target_url` is valid; app_params is not empty;qed " )
. to_owned ( ) ,
2016-12-27 11:15:02 +01:00
} ,
self . embeddable_on . clone ( ) ,
2016-12-22 18:26:39 +01:00
self . remote . clone ( ) ,
self . fetch . clone ( ) ,
) ;
let res = handler . on_request ( request ) ;
self . state = State ::Fetching ( handler ) ;
res
}
fn on_request_readable ( & mut self , decoder : & mut Decoder < net ::HttpStream > ) -> Next {
match self . state {
State ::Initial = > Next ::end ( ) ,
State ::Error ( ref mut handler ) = > handler . on_request_readable ( decoder ) ,
State ::Fetching ( ref mut handler ) = > handler . on_request_readable ( decoder ) ,
}
}
fn on_response ( & mut self , res : & mut server ::Response ) -> Next {
match self . state {
State ::Initial = > Next ::end ( ) ,
State ::Error ( ref mut handler ) = > handler . on_response ( res ) ,
State ::Fetching ( ref mut handler ) = > handler . on_response ( res ) ,
}
}
fn on_response_writable ( & mut self , encoder : & mut Encoder < net ::HttpStream > ) -> Next {
match self . state {
State ::Initial = > Next ::end ( ) ,
State ::Error ( ref mut handler ) = > handler . on_response_writable ( encoder ) ,
State ::Fetching ( ref mut handler ) = > handler . on_response_writable ( encoder ) ,
}
}
}