client/rpc/
extractor.rs

1// Copyright 2015-2019 Parity Technologies (UK) Ltd.
2// This file is part of Parity Ethereum.
3
4// Parity Ethereum is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Parity Ethereum is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Parity Ethereum.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Parity-specific metadata extractors.
18
19use crate::rpc::{http_common::HttpMetaExtractor, Metadata, Origin};
20use cfx_types::H256;
21use jsonrpc_pubsub::Session;
22use jsonrpc_tcp_server as tcp;
23use jsonrpc_ws_server as ws;
24use std::sync::Arc;
25//use ws;
26
27/// Common HTTP & IPC metadata extractor.
28pub struct RpcExtractor;
29
30impl HttpMetaExtractor for RpcExtractor {
31    type Metadata = Metadata;
32
33    fn read_metadata(
34        &self, origin: Option<String>, user_agent: Option<String>,
35    ) -> Metadata {
36        Metadata {
37            origin: Origin::Rpc(format!(
38                "{} / {}",
39                origin.unwrap_or_else(|| "unknown origin".to_string()),
40                user_agent.unwrap_or_else(|| "unknown agent".to_string())
41            )),
42            session: None,
43        }
44    }
45}
46
47impl tcp::MetaExtractor<Metadata> for RpcExtractor {
48    fn extract(&self, req: &tcp::RequestContext) -> Metadata {
49        Metadata {
50            origin: Origin::Tcp(req.peer_addr),
51            session: Some(Arc::new(Session::new(req.sender.clone()))),
52        }
53    }
54}
55
56impl ws::MetaExtractor<Metadata> for RpcExtractor {
57    fn extract(&self, req: &ws::RequestContext) -> Metadata {
58        Metadata {
59            origin: Origin::Ws {
60                session: H256::from_low_u64_be(req.session_id),
61            },
62            session: Some(Arc::new(Session::new(req.sender()))),
63        }
64    }
65}
66
67///// WebSockets server metadata extractor and request middleware.
68//pub struct WsExtractor {
69//    authcodes_path: Option<PathBuf>,
70//}
71//
72//impl WsExtractor {
73//    /// Creates new `WsExtractor` with given authcodes path.
74//    pub fn new(path: Option<&Path>) -> Self {
75//        WsExtractor {
76//            authcodes_path: path.map(ToOwned::to_owned),
77//        }
78//    }
79//}
80//
81//impl ws::MetaExtractor<Metadata> for WsExtractor {
82//    fn extract(&self, req: &ws::RequestContext) -> Metadata {
83//        let id = req.session_id as u64;
84//
85//        let origin = match self.authcodes_path {
86//            Some(ref path) => {
87//                let authorization = req
88//                    .protocols
89//                    .get(0)
90//                    .and_then(|p| auth_token_hash(&path, p, true));
91//                match authorization {
92//                    Some(id) => Origin::Signer { session: id },
93//                    None => Origin::Ws {
94//                        session: H256::from_low_u64_be(id),
95//                    },
96//                }
97//            }
98//            None => Origin::Ws {
99//                session: H256::from_low_u64_be(id),
100//            },
101//        };
102//        let session = Some(Arc::new(Session::new(req.sender())));
103//        Metadata { origin, session }
104//    }
105//}
106//
107//impl ws::RequestMiddleware for WsExtractor {
108//    fn process(&self, req: &ws::ws::Request) -> ws::MiddlewareAction {
109//        use self::ws::ws::Response;
110//
111//        // Reply with 200 OK to HEAD requests.
112//        if req.method() == "HEAD" {
113//            let mut response = Response::new(200, "OK", vec![]);
114//            add_security_headers(&mut response);
115//            return Some(response).into();
116//        }
117//
118//        // Display WS info.
119//        if req.header("sec-websocket-key").is_none() {
120//            let mut response = Response::new(200, "OK", b"WebSocket interface
121// is active. Open WS connection to access RPC.".to_vec());
122// add_security_headers(&mut response);            return Some(response).into();
123//        }
124//
125//        // If protocol is provided it needs to be valid.
126//        let protocols = req.protocols().ok().unwrap_or_else(Vec::new);
127//        if let Some(ref path) = self.authcodes_path {
128//            if protocols.len() == 1 {
129//                let authorization = auth_token_hash(&path, protocols[0],
130// false);                if authorization.is_none() {
131//                    warn!(
132//                        "Blocked connection from {} using invalid token.",
133//                        req.header("origin")
134//                            .and_then(|e| ::std::str::from_utf8(e).ok())
135//                            .unwrap_or("Unknown Origin")
136//                    );
137//                    let mut response = Response::new(403, "Forbidden",
138// vec![]);                    add_security_headers(&mut response);
139//                    return Some(response).into();
140//                }
141//            }
142//        }
143//
144//        // Otherwise just proceed.
145//        ws::MiddlewareAction::Proceed
146//    }
147//}
148//
149//fn add_security_headers(res: &mut ws::ws::Response) {
150//    let headers = res.headers_mut();
151//    headers.push(("X-Frame-Options".into(), b"SAMEORIGIN".to_vec()));
152//    headers.push(("X-XSS-Protection".into(), b"1; mode=block".to_vec()));
153//    headers.push(("X-Content-Type-Options".into(), b"nosniff".to_vec()));
154//    headers.push(("Content-Security-Policy".into(),
155//                  b"default-src 'self';form-action
156// 'none';block-all-mixed-content;sandbox allow-scripts;".to_vec()    ));
157//}
158//
159//fn auth_token_hash(
160//    codes_path: &Path, protocol: &str, save_file: bool,
161//) -> Option<H256> {
162//    let mut split = protocol.split('_');
163//    let auth = split.next().and_then(|v| v.parse().ok());
164//    let time = split.next().and_then(|v| u64::from_str_radix(v, 10).ok());
165//
166//    if let (Some(auth), Some(time)) = (auth, time) {
167//        // Check if the code is valid
168//        return authcodes::AuthCodes::from_file(codes_path)
169//            .ok()
170//            .and_then(|mut codes| {
171//                // remove old tokens
172//                codes.clear_garbage();
173//
174//                let res = codes.is_valid(&auth, time);
175//
176//                if save_file {
177//                    // make sure to save back authcodes - it might have been
178// modified                    if codes.to_file(codes_path).is_err() {
179//                        warn!(target: "signer", "Couldn't save authorization
180// codes to file.");                    }
181//                }
182//
183//                if res {
184//                    Some(auth)
185//                } else {
186//                    None
187//                }
188//            });
189//    }
190//
191//    None
192//}
193//
194///// WebSockets RPC usage statistics.
195//pub struct WsStats {
196//    stats: Arc<RpcStats>,
197//}
198//
199//impl WsStats {
200//    /// Creates new WS usage tracker.
201//    pub fn new(stats: Arc<RpcStats>) -> Self { WsStats { stats } }
202//}
203//
204//impl ws::SessionStats for WsStats {
205//    fn open_session(&self, _id: ws::SessionId) { self.stats.open_session() }
206//
207//    fn close_session(&self, _id: ws::SessionId) { self.stats.close_session() }
208//}
209//
210///// WebSockets middleware dispatching requests to different handles dependning
211///// on metadata.
212//pub struct WsDispatcher<M: core::Middleware<Metadata>> {
213//    full_handler: core::MetaIoHandler<Metadata, M>,
214//}
215//
216//impl<M: core::Middleware<Metadata>> WsDispatcher<M> {
217//    /// Create new `WsDispatcher` with given full handler.
218//    pub fn new(full_handler: core::MetaIoHandler<Metadata, M>) -> Self {
219//        WsDispatcher { full_handler }
220//    }
221//}
222//
223//impl<M: core::Middleware<Metadata>> core::Middleware<Metadata>
224//    for WsDispatcher<M>
225//{
226//    type CallFuture = core::middleware::NoopCallFuture;
227//    type Future = Either<
228//        core::FutureRpcResult<M::Future, M::CallFuture>,
229//        core::FutureResponse,
230//    >;
231//
232//    fn on_request<F, X>(
233//        &self, request: core::Request, meta: Metadata, process: F,
234//    ) -> Either<Self::Future, X>
235//    where
236//        F: FnOnce(core::Request, Metadata) -> X,
237//        X: core::futures::Future<Item = Option<core::Response>, Error = ()>
238//            + Send
239//            + 'static,
240//    {
241//        let use_full = match &meta.origin {
242//            Origin::Signer { .. } => true,
243//            _ => false,
244//        };
245//
246//        if use_full {
247//            Either::A(Either::A(
248//                self.full_handler.handle_rpc_request(request, meta),
249//            ))
250//        } else {
251//            Either::B(process(request, meta))
252//        }
253//    }
254//}
255
256#[cfg(test)]
257mod tests {
258    use super::{HttpMetaExtractor, Origin, RpcExtractor};
259
260    #[test]
261    fn should_extract_rpc_origin() {
262        // given
263        let extractor = RpcExtractor;
264
265        // when
266        let meta1 = extractor.read_metadata(None, None);
267        let meta2 = extractor
268            .read_metadata(None, Some("https://conflux-chain.org".to_owned()));
269        let meta3 = extractor
270            .read_metadata(None, Some("https://conflux-chain.org".to_owned()));
271
272        // then
273        assert_eq!(
274            meta1.origin,
275            Origin::Rpc("unknown origin / unknown agent".into())
276        );
277        assert_eq!(
278            meta2.origin,
279            Origin::Rpc("unknown origin / https://conflux-chain.org".into())
280        );
281        assert_eq!(
282            meta3.origin,
283            Origin::Rpc("unknown origin / https://conflux-chain.org".into())
284        );
285    }
286}