Reformat the source code

This commit is contained in:
Artem Vorotnikov
2020-08-05 07:08:03 +03:00
parent 253ff3f37b
commit 610d9baba4
742 changed files with 175791 additions and 141379 deletions

View File

@@ -14,8 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::ops::{Deref, DerefMut};
use std::path::PathBuf;
use std::{
ops::{Deref, DerefMut},
path::PathBuf,
};
use tempdir::TempDir;
use parity_runtime::{Runtime, TaskExecutor};
@@ -24,64 +26,65 @@ use authcodes::AuthCodes;
/// Server with event loop
pub struct Server<T> {
/// Server
pub server: T,
/// RPC Event Loop
pub event_loop: Runtime,
/// Server
pub server: T,
/// RPC Event Loop
pub event_loop: Runtime,
}
impl<T> Server<T> {
pub fn new<F>(f: F) -> Server<T> where
F: FnOnce(TaskExecutor) -> T,
{
let event_loop = Runtime::with_thread_count(1);
let remote = event_loop.raw_executor();
pub fn new<F>(f: F) -> Server<T>
where
F: FnOnce(TaskExecutor) -> T,
{
let event_loop = Runtime::with_thread_count(1);
let remote = event_loop.raw_executor();
Server {
server: f(remote),
event_loop,
}
}
Server {
server: f(remote),
event_loop,
}
}
}
impl<T> Deref for Server<T> {
type Target = T;
type Target = T;
fn deref(&self) -> &Self::Target {
&self.server
}
fn deref(&self) -> &Self::Target {
&self.server
}
}
/// Struct representing authcodes
pub struct GuardedAuthCodes {
authcodes: AuthCodes,
_tempdir: TempDir,
/// The path to the mock authcodes
pub path: PathBuf,
authcodes: AuthCodes,
_tempdir: TempDir,
/// The path to the mock authcodes
pub path: PathBuf,
}
impl Default for GuardedAuthCodes {
fn default() -> Self {
let tempdir = TempDir::new("").unwrap();
let path = tempdir.path().join("file");
fn default() -> Self {
let tempdir = TempDir::new("").unwrap();
let path = tempdir.path().join("file");
GuardedAuthCodes {
authcodes: AuthCodes::from_file(&path).unwrap(),
_tempdir: tempdir,
path,
}
}
GuardedAuthCodes {
authcodes: AuthCodes::from_file(&path).unwrap(),
_tempdir: tempdir,
path,
}
}
}
impl Deref for GuardedAuthCodes {
type Target = AuthCodes;
fn deref(&self) -> &Self::Target {
&self.authcodes
}
type Target = AuthCodes;
fn deref(&self) -> &Self::Target {
&self.authcodes
}
}
impl DerefMut for GuardedAuthCodes {
fn deref_mut(&mut self) -> &mut AuthCodes {
&mut self.authcodes
}
fn deref_mut(&mut self) -> &mut AuthCodes {
&mut self.authcodes
}
}

View File

@@ -14,119 +14,134 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use std::thread;
use std::time::Duration;
use std::io::{self, Read, Write};
use std::str::{self, Lines};
use std::net::{TcpStream, SocketAddr};
use std::{
io::{self, Read, Write},
net::{SocketAddr, TcpStream},
str::{self, Lines},
thread,
time::Duration,
};
pub struct Response {
pub status: String,
pub headers: Vec<String>,
pub headers_raw: String,
pub body: String,
pub status: String,
pub headers: Vec<String>,
pub headers_raw: String,
pub body: String,
}
impl Response {
pub fn assert_header(&self, header: &str, value: &str) {
let header = format!("{}: {}", header, value);
assert!(self.headers.iter().any(|h| h == &header), "Couldn't find header {} in {:?}", header, &self.headers)
}
pub fn assert_header(&self, header: &str, value: &str) {
let header = format!("{}: {}", header, value);
assert!(
self.headers.iter().any(|h| h == &header),
"Couldn't find header {} in {:?}",
header,
&self.headers
)
}
pub fn assert_status(&self, status: &str) {
assert_eq!(self.status, status.to_owned(), "Got unexpected code. Body: {:?}", self.body);
}
pub fn assert_status(&self, status: &str) {
assert_eq!(
self.status,
status.to_owned(),
"Got unexpected code. Body: {:?}",
self.body
);
}
pub fn assert_security_headers_present(&self, port: Option<u16>) {
assert_security_headers_present(&self.headers, port)
}
pub fn assert_security_headers_present(&self, port: Option<u16>) {
assert_security_headers_present(&self.headers, port)
}
}
pub fn read_block(lines: &mut Lines, all: bool) -> String {
let mut block = String::new();
loop {
let line = lines.next();
match line {
None => break,
Some("") if !all => break,
Some(v) => {
block.push_str(v);
block.push_str("\n");
},
}
}
block
let mut block = String::new();
loop {
let line = lines.next();
match line {
None => break,
Some("") if !all => break,
Some(v) => {
block.push_str(v);
block.push_str("\n");
}
}
}
block
}
fn connect(address: &SocketAddr) -> TcpStream {
let mut retries = 0;
let mut last_error = None;
while retries < 10 {
retries += 1;
let mut retries = 0;
let mut last_error = None;
while retries < 10 {
retries += 1;
let res = TcpStream::connect(address);
match res {
Ok(stream) => {
return stream;
},
Err(e) => {
last_error = Some(e);
thread::sleep(Duration::from_millis(retries * 10));
}
}
}
panic!("Unable to connect to the server. Last error: {:?}", last_error);
let res = TcpStream::connect(address);
match res {
Ok(stream) => {
return stream;
}
Err(e) => {
last_error = Some(e);
thread::sleep(Duration::from_millis(retries * 10));
}
}
}
panic!(
"Unable to connect to the server. Last error: {:?}",
last_error
);
}
pub fn request(address: &SocketAddr, request: &str) -> Response {
let mut req = connect(address);
req.set_read_timeout(Some(Duration::from_secs(2))).unwrap();
req.write_all(request.as_bytes()).unwrap();
let mut req = connect(address);
req.set_read_timeout(Some(Duration::from_secs(2))).unwrap();
req.write_all(request.as_bytes()).unwrap();
let mut response = Vec::new();
loop {
let mut chunk = [0; 32 *1024];
match req.read(&mut chunk) {
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => break,
Err(err) => panic!("Unable to read response: {:?}", err),
Ok(0) => break,
Ok(read) => response.extend_from_slice(&chunk[..read]),
}
}
let mut response = Vec::new();
loop {
let mut chunk = [0; 32 * 1024];
match req.read(&mut chunk) {
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => break,
Err(err) => panic!("Unable to read response: {:?}", err),
Ok(0) => break,
Ok(read) => response.extend_from_slice(&chunk[..read]),
}
}
let response = String::from_utf8_lossy(&response).into_owned();
let mut lines = response.lines();
let status = lines.next().expect("Expected a response").to_owned();
let headers_raw = read_block(&mut lines, false);
let headers = headers_raw.split('\n').map(ToOwned::to_owned).collect();
let body = read_block(&mut lines, true);
let response = String::from_utf8_lossy(&response).into_owned();
let mut lines = response.lines();
let status = lines.next().expect("Expected a response").to_owned();
let headers_raw = read_block(&mut lines, false);
let headers = headers_raw.split('\n').map(ToOwned::to_owned).collect();
let body = read_block(&mut lines, true);
Response {
status,
headers,
headers_raw,
body,
}
Response {
status,
headers,
headers_raw,
body,
}
}
/// Check if all required security headers are present
pub fn assert_security_headers_present(headers: &[String], port: Option<u16>) {
if port.is_none() {
assert!(
headers.iter().any(|header| header.as_str() == "X-Frame-Options: SAMEORIGIN")
"X-Frame-Options: SAMEORIGIN missing: {:?}", headers
);
}
assert!(
headers.iter().any(|header| header.as_str() == "X-XSS-Protection: 1; mode=block")
"X-XSS-Protection missing: {:?}", headers
);
assert!(
headers.iter().any(|header| header.as_str() == "X-Content-Type-Options: nosniff")
"X-Content-Type-Options missing: {:?}", headers
);
assert!(
headers.iter().any(|header| header.starts_with("Content-Security-Policy: "))
"Content-Security-Policy missing: {:?}", headers
)
if port.is_none() {
assert!(
headers.iter().any(|header| header.as_str() == "X-Frame-Options: SAMEORIGIN")
"X-Frame-Options: SAMEORIGIN missing: {:?}", headers
);
}
assert!(
headers.iter().any(|header| header.as_str() == "X-XSS-Protection: 1; mode=block")
"X-XSS-Protection missing: {:?}", headers
);
assert!(
headers.iter().any(|header| header.as_str() == "X-Content-Type-Options: nosniff")
"X-Content-Type-Options missing: {:?}", headers
);
assert!(
headers.iter().any(|header| header.starts_with("Content-Security-Policy: "))
"Content-Security-Policy missing: {:?}", headers
)
}

View File

@@ -18,5 +18,6 @@
mod helpers;
mod http_client;
#[cfg(test)] mod rpc;
#[cfg(test)]
mod rpc;
pub mod ws;

View File

@@ -14,68 +14,70 @@
// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.
use jsonrpc_core::MetaIoHandler;
use http::{self, hyper};
use jsonrpc_core::MetaIoHandler;
use {HttpServer};
use tests::helpers::Server;
use tests::http_client;
use tests::{helpers::Server, http_client};
use v1::{extractors, Metadata};
use HttpServer;
fn serve(handler: Option<MetaIoHandler<Metadata>>) -> Server<HttpServer> {
let address = "127.0.0.1:0".parse().unwrap();
let handler = handler.unwrap_or_default();
let address = "127.0.0.1:0".parse().unwrap();
let handler = handler.unwrap_or_default();
Server::new(|_remote| ::start_http_with_middleware(
&address,
http::DomainsValidation::Disabled,
http::DomainsValidation::Disabled,
handler,
extractors::RpcExtractor,
|request: hyper::Request<hyper::Body>| {
http::RequestMiddlewareAction::Proceed {
should_continue_on_invalid_cors: false,
request,
}
},
1,
5,
false,
).unwrap())
Server::new(|_remote| {
::start_http_with_middleware(
&address,
http::DomainsValidation::Disabled,
http::DomainsValidation::Disabled,
handler,
extractors::RpcExtractor,
|request: hyper::Request<hyper::Body>| http::RequestMiddlewareAction::Proceed {
should_continue_on_invalid_cors: false,
request,
},
1,
5,
false,
)
.unwrap()
})
}
/// Test a single request to running server
fn request(server: Server<HttpServer>, request: &str) -> http_client::Response {
http_client::request(server.server.address(), request)
http_client::request(server.server.address(), request)
}
#[cfg(test)]
mod tests {
use jsonrpc_core::{MetaIoHandler, Value};
use v1::Metadata;
use super::{request, Server};
use super::{request, Server};
use jsonrpc_core::{MetaIoHandler, Value};
use v1::Metadata;
fn serve() -> (Server<::HttpServer>, ::std::net::SocketAddr) {
let mut io = MetaIoHandler::default();
io.add_method_with_meta("hello", |_, meta: Metadata| {
Ok(Value::String(format!("{}", meta.origin)))
});
let server = super::serve(Some(io));
let address = server.server.address().to_owned();
fn serve() -> (Server<::HttpServer>, ::std::net::SocketAddr) {
let mut io = MetaIoHandler::default();
io.add_method_with_meta("hello", |_, meta: Metadata| {
Ok(Value::String(format!("{}", meta.origin)))
});
let server = super::serve(Some(io));
let address = server.server.address().to_owned();
(server, address)
}
(server, address)
}
#[test]
fn should_extract_rpc_origin() {
// given
let (server, address) = serve();
#[test]
fn should_extract_rpc_origin() {
// given
let (server, address) = serve();
// when
let req = r#"{"method":"hello","params":[],"jsonrpc":"2.0","id":1}"#;
let expected = "{\"jsonrpc\":\"2.0\",\"result\":\"unknown origin / unknown agent via RPC\",\"id\":1}\n";
let res = request(server,
&format!("\
// when
let req = r#"{"method":"hello","params":[],"jsonrpc":"2.0","id":1}"#;
let expected = "{\"jsonrpc\":\"2.0\",\"result\":\"unknown origin / unknown agent via RPC\",\"id\":1}\n";
let res = request(
server,
&format!(
"\
POST / HTTP/1.1\r\n\
Host: {}\r\n\
Content-Type: application/json\r\n\
@@ -83,24 +85,31 @@ mod tests {
Connection: close\r\n\
\r\n\
{}
", address, req.len(), req)
);
",
address,
req.len(),
req
),
);
// then
res.assert_status("HTTP/1.1 200 OK");
assert_eq!(res.body, expected);
}
// then
res.assert_status("HTTP/1.1 200 OK");
assert_eq!(res.body, expected);
}
#[test]
fn should_extract_rpc_origin_with_service() {
// given
let (server, address) = serve();
#[test]
fn should_extract_rpc_origin_with_service() {
// given
let (server, address) = serve();
// when
let req = r#"{"method":"hello","params":[],"jsonrpc":"2.0","id":1}"#;
let expected = "{\"jsonrpc\":\"2.0\",\"result\":\"unknown origin / curl/7.16.3 via RPC\",\"id\":1}\n";
let res = request(server,
&format!("\
// when
let req = r#"{"method":"hello","params":[],"jsonrpc":"2.0","id":1}"#;
let expected =
"{\"jsonrpc\":\"2.0\",\"result\":\"unknown origin / curl/7.16.3 via RPC\",\"id\":1}\n";
let res = request(
server,
&format!(
"\
POST / HTTP/1.1\r\n\
Host: {}\r\n\
Content-Type: application/json\r\n\
@@ -109,23 +118,29 @@ mod tests {
User-Agent: curl/7.16.3\r\n\
\r\n\
{}
", address, req.len(), req)
);
",
address,
req.len(),
req
),
);
// then
res.assert_status("HTTP/1.1 200 OK");
assert_eq!(res.body, expected);
}
// then
res.assert_status("HTTP/1.1 200 OK");
assert_eq!(res.body, expected);
}
#[test]
fn should_respond_valid_to_any_requested_header() {
// given
let (server, address) = serve();
let headers = "Something, Anything, Xyz, 123, _?";
#[test]
fn should_respond_valid_to_any_requested_header() {
// given
let (server, address) = serve();
let headers = "Something, Anything, Xyz, 123, _?";
// when
let res = request(server,
&format!("\
// when
let res = request(
server,
&format!(
"\
OPTIONS / HTTP/1.1\r\n\
Host: {}\r\n\
Origin: http://parity.io\r\n\
@@ -134,13 +149,18 @@ mod tests {
Connection: close\r\n\
Access-Control-Request-Headers: {}\r\n\
\r\n\
", address, headers)
);
// then
assert_eq!(res.status, "HTTP/1.1 200 OK".to_owned());
let expected = format!("access-control-allow-headers: {}", headers);
assert!(res.headers.contains(&expected), "Headers missing in {:?}", res.headers);
}
",
address, headers
),
);
// then
assert_eq!(res.status, "HTTP/1.1 200 OK".to_owned());
let expected = format!("access-control-allow-headers: {}", headers);
assert!(
res.headers.contains(&expected),
"Headers missing in {:?}",
res.headers
);
}
}

View File

@@ -21,71 +21,82 @@ use std::sync::Arc;
use jsonrpc_core::MetaIoHandler;
use ws;
use tests::{
helpers::{GuardedAuthCodes, Server},
http_client,
};
use v1::{extractors, informant};
use tests::helpers::{GuardedAuthCodes, Server};
use tests::http_client;
/// Setup a mock signer for tests
pub fn serve() -> (Server<ws::Server>, usize, GuardedAuthCodes) {
let address = "127.0.0.1:0".parse().unwrap();
let io = MetaIoHandler::default();
let authcodes = GuardedAuthCodes::default();
let stats = Arc::new(informant::RpcStats::default());
let address = "127.0.0.1:0".parse().unwrap();
let io = MetaIoHandler::default();
let authcodes = GuardedAuthCodes::default();
let stats = Arc::new(informant::RpcStats::default());
let res = Server::new(|_| ::start_ws(
&address,
io,
ws::DomainsValidation::Disabled,
ws::DomainsValidation::Disabled,
5,
extractors::WsExtractor::new(Some(&authcodes.path)),
extractors::WsExtractor::new(Some(&authcodes.path)),
extractors::WsStats::new(stats),
).unwrap());
let port = res.addr().port() as usize;
let res = Server::new(|_| {
::start_ws(
&address,
io,
ws::DomainsValidation::Disabled,
ws::DomainsValidation::Disabled,
5,
extractors::WsExtractor::new(Some(&authcodes.path)),
extractors::WsExtractor::new(Some(&authcodes.path)),
extractors::WsStats::new(stats),
)
.unwrap()
});
let port = res.addr().port() as usize;
(res, port, authcodes)
(res, port, authcodes)
}
/// Test a single request to running server
pub fn request(server: Server<ws::Server>, request: &str) -> http_client::Response {
http_client::request(server.server.addr(), request)
http_client::request(server.server.addr(), request)
}
#[cfg(test)]
mod testing {
use std::time;
use hash::keccak;
use super::{serve, request, http_client};
use super::{http_client, request, serve};
use hash::keccak;
use std::time;
#[test]
fn should_not_redirect_to_parity_host() {
// given
let (server, port, _) = serve();
#[test]
fn should_not_redirect_to_parity_host() {
// given
let (server, port, _) = serve();
// when
let response = request(server,
&format!("\
// when
let response = request(
server,
&format!(
"\
GET / HTTP/1.1\r\n\
Host: 127.0.0.1:{}\r\n\
Connection: close\r\n\
\r\n\
{{}}
", port)
);
",
port
),
);
// then
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
}
// then
assert_eq!(response.status, "HTTP/1.1 200 OK".to_owned());
}
#[test]
fn should_block_if_authorization_is_incorrect() {
// given
let (server, port, _) = serve();
#[test]
fn should_block_if_authorization_is_incorrect() {
// given
let (server, port, _) = serve();
// when
let response = request(server,
&format!("\
// when
let response = request(
server,
&format!(
"\
GET / HTTP/1.1\r\n\
Host: 127.0.0.1:{}\r\n\
Connection: Upgrade\r\n\
@@ -94,26 +105,30 @@ mod testing {
Sec-WebSocket-Version: 13\r\n\
\r\n\
{{}}
", port)
);
",
port
),
);
// then
assert_eq!(response.status, "HTTP/1.1 403 Forbidden".to_owned());
http_client::assert_security_headers_present(&response.headers, None);
}
// then
assert_eq!(response.status, "HTTP/1.1 403 Forbidden".to_owned());
http_client::assert_security_headers_present(&response.headers, None);
}
#[cfg(not(target_os = "windows"))]
#[test]
fn should_allow_if_authorization_is_correct() {
// given
let (server, port, mut authcodes) = serve();
let code = authcodes.generate_new().unwrap().replace("-", "");
authcodes.to_file(&authcodes.path).unwrap();
let timestamp = time::UNIX_EPOCH.elapsed().unwrap().as_secs();
#[cfg(not(target_os = "windows"))]
#[test]
fn should_allow_if_authorization_is_correct() {
// given
let (server, port, mut authcodes) = serve();
let code = authcodes.generate_new().unwrap().replace("-", "");
authcodes.to_file(&authcodes.path).unwrap();
let timestamp = time::UNIX_EPOCH.elapsed().unwrap().as_secs();
// when
let response = request(server,
&format!("\
// when
let response = request(
server,
&format!(
"\
GET / HTTP/1.1\r\n\
Host: 127.0.0.1:{}\r\n\
Connection: Close\r\n\
@@ -123,27 +138,32 @@ mod testing {
\r\n\
{{}}
",
port,
keccak(format!("{}:{}", code, timestamp)),
timestamp,
)
);
port,
keccak(format!("{}:{}", code, timestamp)),
timestamp,
),
);
// then
assert_eq!(response.status, "HTTP/1.1 101 Switching Protocols".to_owned());
}
// then
assert_eq!(
response.status,
"HTTP/1.1 101 Switching Protocols".to_owned()
);
}
#[test]
fn should_not_allow_initial_connection_even_once() {
// given
let (server, port, authcodes) = serve();
let code = "initial";
let timestamp = time::UNIX_EPOCH.elapsed().unwrap().as_secs();
assert!(authcodes.is_empty());
#[test]
fn should_not_allow_initial_connection_even_once() {
// given
let (server, port, authcodes) = serve();
let code = "initial";
let timestamp = time::UNIX_EPOCH.elapsed().unwrap().as_secs();
assert!(authcodes.is_empty());
// when
let response1 = http_client::request(server.addr(),
&format!("\
// when
let response1 = http_client::request(
server.addr(),
&format!(
"\
GET / HTTP/1.1\r\n\
Host: 127.0.0.1:{}\r\n\
Connection: Close\r\n\
@@ -153,14 +173,14 @@ mod testing {
\r\n\
{{}}
",
port,
keccak(format!("{}:{}", code, timestamp)),
timestamp,
)
);
port,
keccak(format!("{}:{}", code, timestamp)),
timestamp,
),
);
// then
assert_eq!(response1.status, "HTTP/1.1 403 Forbidden".to_owned());
http_client::assert_security_headers_present(&response1.headers, None);
}
// then
assert_eq!(response1.status, "HTTP/1.1 403 Forbidden".to_owned());
http_client::assert_security_headers_present(&response1.headers, None);
}
}