More CLI settings for IPFS API (#4608)
* TEMP: Bind to 0.0.0.0, don't check Origin * More CLI options for IPFS * CORS and Hosts filtering * Allow current interface as origin * Correctly handle CORS settings * fix grumbles
This commit is contained in:
parent
9b5bcb81fd
commit
f97e775498
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1640,6 +1640,7 @@ dependencies = [
|
|||||||
"ethcore 1.6.0",
|
"ethcore 1.6.0",
|
||||||
"ethcore-util 1.6.0",
|
"ethcore-util 1.6.0",
|
||||||
"hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)",
|
"hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)",
|
||||||
|
"jsonrpc-http-server 6.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
||||||
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"mime 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"multihash 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"multihash 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rlp 0.1.0",
|
"rlp 0.1.0",
|
||||||
|
@ -8,6 +8,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
ethcore = { path = "../ethcore" }
|
ethcore = { path = "../ethcore" }
|
||||||
ethcore-util = { path = "../util" }
|
ethcore-util = { path = "../util" }
|
||||||
|
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc.git" }
|
||||||
rlp = { path = "../util/rlp" }
|
rlp = { path = "../util/rlp" }
|
||||||
mime = "0.2"
|
mime = "0.2"
|
||||||
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }
|
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use {multihash, cid, hyper};
|
use {multihash, cid, hyper};
|
||||||
use handler::Out;
|
use route::Out;
|
||||||
|
|
||||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||||
|
|
||||||
@ -26,6 +26,8 @@ pub enum ServerError {
|
|||||||
IoError(::std::io::Error),
|
IoError(::std::io::Error),
|
||||||
/// Other `hyper` error
|
/// Other `hyper` error
|
||||||
Other(hyper::error::Error),
|
Other(hyper::error::Error),
|
||||||
|
/// Invalid --ipfs-api-interface
|
||||||
|
InvalidInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
@ -89,6 +91,7 @@ impl From<ServerError> for String {
|
|||||||
match err {
|
match err {
|
||||||
ServerError::IoError(err) => err.to_string(),
|
ServerError::IoError(err) => err.to_string(),
|
||||||
ServerError::Other(err) => err.to_string(),
|
ServerError::Other(err) => err.to_string(),
|
||||||
|
ServerError::InvalidInterface => "Invalid --ipfs-api-interface parameter".into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
139
ipfs/src/lib.rs
139
ipfs/src/lib.rs
@ -23,21 +23,91 @@ extern crate cid;
|
|||||||
extern crate rlp;
|
extern crate rlp;
|
||||||
extern crate ethcore;
|
extern crate ethcore;
|
||||||
extern crate ethcore_util as util;
|
extern crate ethcore_util as util;
|
||||||
|
extern crate jsonrpc_http_server;
|
||||||
|
|
||||||
mod error;
|
pub mod error;
|
||||||
mod handler;
|
mod route;
|
||||||
|
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::net::{SocketAddr, IpAddr, Ipv4Addr};
|
use std::net::{SocketAddr, IpAddr};
|
||||||
use error::ServerError;
|
use error::ServerError;
|
||||||
use handler::{IpfsHandler, Out};
|
use route::Out;
|
||||||
|
use jsonrpc_http_server::cors;
|
||||||
use hyper::server::{Listening, Handler, Request, Response};
|
use hyper::server::{Listening, Handler, Request, Response};
|
||||||
use hyper::net::HttpStream;
|
use hyper::net::HttpStream;
|
||||||
use hyper::header::{ContentLength, ContentType, Origin};
|
use hyper::header::{Vary, ContentLength, ContentType, AccessControlAllowOrigin};
|
||||||
use hyper::{Next, Encoder, Decoder, Method, RequestUri, StatusCode};
|
use hyper::{Next, Encoder, Decoder, Method, RequestUri, StatusCode};
|
||||||
use ethcore::client::BlockChainClient;
|
use ethcore::client::BlockChainClient;
|
||||||
|
|
||||||
|
|
||||||
|
/// Request/response handler
|
||||||
|
pub struct IpfsHandler {
|
||||||
|
/// Response to send out
|
||||||
|
out: Out,
|
||||||
|
/// How many bytes from the response have been written
|
||||||
|
out_progress: usize,
|
||||||
|
/// Origin request header
|
||||||
|
origin: Option<String>,
|
||||||
|
/// Allowed CORS domains
|
||||||
|
cors_domains: Option<Vec<AccessControlAllowOrigin>>,
|
||||||
|
/// Hostnames allowed in the `Host` request header
|
||||||
|
allowed_hosts: Option<Vec<String>>,
|
||||||
|
/// Reference to the Blockchain Client
|
||||||
|
client: Arc<BlockChainClient>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IpfsHandler {
|
||||||
|
pub fn client(&self) -> &BlockChainClient {
|
||||||
|
&*self.client
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(cors: Option<Vec<String>>, hosts: Option<Vec<String>>, client: Arc<BlockChainClient>) -> Self {
|
||||||
|
fn origin_to_header(origin: String) -> AccessControlAllowOrigin {
|
||||||
|
match origin.as_str() {
|
||||||
|
"*" => AccessControlAllowOrigin::Any,
|
||||||
|
"null" | "" => AccessControlAllowOrigin::Null,
|
||||||
|
_ => AccessControlAllowOrigin::Value(origin),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IpfsHandler {
|
||||||
|
out: Out::Bad("Invalid Request"),
|
||||||
|
out_progress: 0,
|
||||||
|
origin: None,
|
||||||
|
cors_domains: cors.map(|vec| vec.into_iter().map(origin_to_header).collect()),
|
||||||
|
allowed_hosts: hosts,
|
||||||
|
client: client,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_host_allowed(&self, req: &Request<HttpStream>) -> bool {
|
||||||
|
match self.allowed_hosts {
|
||||||
|
Some(ref hosts) => jsonrpc_http_server::is_host_header_valid(&req, hosts),
|
||||||
|
None => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_origin_allowed(&self) -> bool {
|
||||||
|
// Check origin header first, no header passed is good news
|
||||||
|
let origin = match self.origin {
|
||||||
|
Some(ref origin) => origin,
|
||||||
|
None => return true,
|
||||||
|
};
|
||||||
|
|
||||||
|
let cors_domains = match self.cors_domains {
|
||||||
|
Some(ref domains) => domains,
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
cors_domains.iter().any(|domain| match *domain {
|
||||||
|
AccessControlAllowOrigin::Value(ref allowed) => origin == allowed,
|
||||||
|
AccessControlAllowOrigin::Any => true,
|
||||||
|
AccessControlAllowOrigin::Null => origin == "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Implement Hyper's HTTP handler
|
/// Implement Hyper's HTTP handler
|
||||||
impl Handler<HttpStream> for IpfsHandler {
|
impl Handler<HttpStream> for IpfsHandler {
|
||||||
fn on_request(&mut self, req: Request<HttpStream>) -> Next {
|
fn on_request(&mut self, req: Request<HttpStream>) -> Next {
|
||||||
@ -45,9 +115,16 @@ impl Handler<HttpStream> for IpfsHandler {
|
|||||||
return Next::write();
|
return Next::write();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reject requests if the Origin header isn't valid
|
self.origin = cors::read_origin(&req);
|
||||||
if req.headers().get::<Origin>().map(|o| "127.0.0.1" != &o.host.hostname).unwrap_or(false) {
|
|
||||||
self.out = Out::Bad("Illegal Origin");
|
if !self.is_host_allowed(&req) {
|
||||||
|
self.out = Out::Bad("Disallowed Host header");
|
||||||
|
|
||||||
|
return Next::write();
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.is_origin_allowed() {
|
||||||
|
self.out = Out::Bad("Disallowed Origin header");
|
||||||
|
|
||||||
return Next::write();
|
return Next::write();
|
||||||
}
|
}
|
||||||
@ -57,7 +134,9 @@ impl Handler<HttpStream> for IpfsHandler {
|
|||||||
_ => return Next::write(),
|
_ => return Next::write(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.route(path, query)
|
self.out = self.route(path, query);
|
||||||
|
|
||||||
|
Next::write()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
|
fn on_request_readable(&mut self, _decoder: &mut Decoder<HttpStream>) -> Next {
|
||||||
@ -82,26 +161,28 @@ impl Handler<HttpStream> for IpfsHandler {
|
|||||||
res.headers_mut().set(ContentLength(bytes.len() as u64));
|
res.headers_mut().set(ContentLength(bytes.len() as u64));
|
||||||
res.headers_mut().set(ContentType(content_type));
|
res.headers_mut().set(ContentType(content_type));
|
||||||
|
|
||||||
Next::write()
|
|
||||||
},
|
},
|
||||||
NotFound(reason) => {
|
NotFound(reason) => {
|
||||||
res.set_status(StatusCode::NotFound);
|
res.set_status(StatusCode::NotFound);
|
||||||
|
|
||||||
res.headers_mut().set(ContentLength(reason.len() as u64));
|
res.headers_mut().set(ContentLength(reason.len() as u64));
|
||||||
res.headers_mut().set(ContentType(mime!(Text/Plain)));
|
res.headers_mut().set(ContentType(mime!(Text/Plain)));
|
||||||
|
|
||||||
Next::write()
|
|
||||||
},
|
},
|
||||||
Bad(reason) => {
|
Bad(reason) => {
|
||||||
res.set_status(StatusCode::BadRequest);
|
res.set_status(StatusCode::BadRequest);
|
||||||
|
|
||||||
res.headers_mut().set(ContentLength(reason.len() as u64));
|
res.headers_mut().set(ContentLength(reason.len() as u64));
|
||||||
res.headers_mut().set(ContentType(mime!(Text/Plain)));
|
res.headers_mut().set(ContentType(mime!(Text/Plain)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(cors_header) = cors::get_cors_header(&self.cors_domains, &self.origin) {
|
||||||
|
res.headers_mut().set(cors_header);
|
||||||
|
res.headers_mut().set(Vary::Items(vec!["Origin".into()]));
|
||||||
|
}
|
||||||
|
|
||||||
Next::write()
|
Next::write()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_response_writable(&mut self, transport: &mut Encoder<HttpStream>) -> Next {
|
fn on_response_writable(&mut self, transport: &mut Encoder<HttpStream>) -> Next {
|
||||||
use Out::*;
|
use Out::*;
|
||||||
@ -116,11 +197,12 @@ impl Handler<HttpStream> for IpfsHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempt to write entire `data` from current `progress`
|
||||||
fn write_chunk<W: Write>(transport: &mut W, progress: &mut usize, data: &[u8]) -> Next {
|
fn write_chunk<W: Write>(transport: &mut W, progress: &mut usize, data: &[u8]) -> Next {
|
||||||
// Skip any bytes that have already been written
|
// Skip any bytes that have already been written
|
||||||
let chunk = &data[*progress..];
|
let chunk = &data[*progress..];
|
||||||
|
|
||||||
// Write an get written count
|
// Write an get the amount of bytes written. End the connection in case of an error.
|
||||||
let written = match transport.write(chunk) {
|
let written = match transport.write(chunk) {
|
||||||
Ok(written) => written,
|
Ok(written) => written,
|
||||||
Err(_) => return Next::end(),
|
Err(_) => return Next::end(),
|
||||||
@ -128,7 +210,7 @@ fn write_chunk<W: Write>(transport: &mut W, progress: &mut usize, data: &[u8]) -
|
|||||||
|
|
||||||
*progress += written;
|
*progress += written;
|
||||||
|
|
||||||
// Close the connection if the entire chunk has been written, otherwise increment progress
|
// Close the connection if the entire remaining chunk has been written
|
||||||
if written < chunk.len() {
|
if written < chunk.len() {
|
||||||
Next::write()
|
Next::write()
|
||||||
} else {
|
} else {
|
||||||
@ -136,12 +218,31 @@ fn write_chunk<W: Write>(transport: &mut W, progress: &mut usize, data: &[u8]) -
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_server(port: u16, client: Arc<BlockChainClient>) -> Result<Listening, ServerError> {
|
/// Add current interface (default: "127.0.0.1:5001") to list of allowed hosts
|
||||||
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), port);
|
fn include_current_interface(mut hosts: Vec<String>, interface: String, port: u16) -> Vec<String> {
|
||||||
|
hosts.push(match port {
|
||||||
|
80 => interface,
|
||||||
|
_ => format!("{}:{}", interface, port),
|
||||||
|
});
|
||||||
|
|
||||||
|
hosts
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_server(
|
||||||
|
port: u16,
|
||||||
|
interface: String,
|
||||||
|
cors: Option<Vec<String>>,
|
||||||
|
hosts: Option<Vec<String>>,
|
||||||
|
client: Arc<BlockChainClient>
|
||||||
|
) -> Result<Listening, ServerError> {
|
||||||
|
|
||||||
|
let ip: IpAddr = interface.parse().map_err(|_| ServerError::InvalidInterface)?;
|
||||||
|
let addr = SocketAddr::new(ip, port);
|
||||||
|
let hosts = hosts.map(move |hosts| include_current_interface(hosts, interface, port));
|
||||||
|
|
||||||
Ok(
|
Ok(
|
||||||
hyper::Server::http(&addr)?
|
hyper::Server::http(&addr)?
|
||||||
.handle(move |_| IpfsHandler::new(client.clone()))
|
.handle(move |_| IpfsHandler::new(cors.clone(), hosts.clone(), client.clone()))
|
||||||
.map(|(listening, srv)| {
|
.map(|(listening, srv)| {
|
||||||
|
|
||||||
::std::thread::spawn(move || {
|
::std::thread::spawn(move || {
|
||||||
|
@ -14,15 +14,13 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use {rlp, multihash};
|
use {rlp, multihash, IpfsHandler};
|
||||||
use error::{Error, Result};
|
use error::{Error, Result};
|
||||||
use cid::{ToCid, Codec};
|
use cid::{ToCid, Codec};
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
use multihash::Hash;
|
use multihash::Hash;
|
||||||
use hyper::Next;
|
|
||||||
use util::{Bytes, H256};
|
use util::{Bytes, H256};
|
||||||
use ethcore::client::{BlockId, TransactionId, BlockChainClient};
|
use ethcore::client::{BlockId, TransactionId};
|
||||||
|
|
||||||
type Reason = &'static str;
|
type Reason = &'static str;
|
||||||
|
|
||||||
@ -34,30 +32,10 @@ pub enum Out {
|
|||||||
Bad(Reason),
|
Bad(Reason),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request/response handler
|
|
||||||
pub struct IpfsHandler {
|
|
||||||
/// Reference to the Blockchain Client
|
|
||||||
client: Arc<BlockChainClient>,
|
|
||||||
|
|
||||||
/// Response to send out
|
|
||||||
pub out: Out,
|
|
||||||
|
|
||||||
/// How many bytes from the response have been written
|
|
||||||
pub out_progress: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IpfsHandler {
|
impl IpfsHandler {
|
||||||
pub fn new(client: Arc<BlockChainClient>) -> Self {
|
|
||||||
IpfsHandler {
|
|
||||||
client: client,
|
|
||||||
out: Out::Bad("Invalid Request"),
|
|
||||||
out_progress: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Route path + query string to a specialized method
|
/// Route path + query string to a specialized method
|
||||||
pub fn route(&mut self, path: &str, query: Option<&str>) -> Next {
|
pub fn route(&self, path: &str, query: Option<&str>) -> Out {
|
||||||
self.out = match path {
|
match path {
|
||||||
"/api/v0/block/get" => {
|
"/api/v0/block/get" => {
|
||||||
let arg = query.and_then(|q| get_param(q, "arg")).unwrap_or("");
|
let arg = query.and_then(|q| get_param(q, "arg")).unwrap_or("");
|
||||||
|
|
||||||
@ -65,9 +43,7 @@ impl IpfsHandler {
|
|||||||
},
|
},
|
||||||
|
|
||||||
_ => Out::NotFound("Route not found")
|
_ => Out::NotFound("Route not found")
|
||||||
};
|
}
|
||||||
|
|
||||||
Next::write()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to read Content ID from `arg` query parameter, get a hash and
|
/// Attempt to read Content ID from `arg` query parameter, get a hash and
|
||||||
@ -94,14 +70,14 @@ impl IpfsHandler {
|
|||||||
/// Get block header by hash as raw binary.
|
/// Get block header by hash as raw binary.
|
||||||
fn block(&self, hash: H256) -> Result<Out> {
|
fn block(&self, hash: H256) -> Result<Out> {
|
||||||
let block_id = BlockId::Hash(hash);
|
let block_id = BlockId::Hash(hash);
|
||||||
let block = self.client.block_header(block_id).ok_or(Error::BlockNotFound)?;
|
let block = self.client().block_header(block_id).ok_or(Error::BlockNotFound)?;
|
||||||
|
|
||||||
Ok(Out::OctetStream(block.into_inner()))
|
Ok(Out::OctetStream(block.into_inner()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get list of block ommers by hash as raw binary.
|
/// Get list of block ommers by hash as raw binary.
|
||||||
fn block_list(&self, hash: H256) -> Result<Out> {
|
fn block_list(&self, hash: H256) -> Result<Out> {
|
||||||
let uncles = self.client.find_uncles(&hash).ok_or(Error::BlockNotFound)?;
|
let uncles = self.client().find_uncles(&hash).ok_or(Error::BlockNotFound)?;
|
||||||
|
|
||||||
Ok(Out::OctetStream(rlp::encode(&uncles).to_vec()))
|
Ok(Out::OctetStream(rlp::encode(&uncles).to_vec()))
|
||||||
}
|
}
|
||||||
@ -109,21 +85,21 @@ impl IpfsHandler {
|
|||||||
/// Get transaction by hash and return as raw binary.
|
/// Get transaction by hash and return as raw binary.
|
||||||
fn transaction(&self, hash: H256) -> Result<Out> {
|
fn transaction(&self, hash: H256) -> Result<Out> {
|
||||||
let tx_id = TransactionId::Hash(hash);
|
let tx_id = TransactionId::Hash(hash);
|
||||||
let tx = self.client.transaction(tx_id).ok_or(Error::TransactionNotFound)?;
|
let tx = self.client().transaction(tx_id).ok_or(Error::TransactionNotFound)?;
|
||||||
|
|
||||||
Ok(Out::OctetStream(rlp::encode(&*tx).to_vec()))
|
Ok(Out::OctetStream(rlp::encode(&*tx).to_vec()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get state trie node by hash and return as raw binary.
|
/// Get state trie node by hash and return as raw binary.
|
||||||
fn state_trie(&self, hash: H256) -> Result<Out> {
|
fn state_trie(&self, hash: H256) -> Result<Out> {
|
||||||
let data = self.client.state_data(&hash).ok_or(Error::StateRootNotFound)?;
|
let data = self.client().state_data(&hash).ok_or(Error::StateRootNotFound)?;
|
||||||
|
|
||||||
Ok(Out::OctetStream(data))
|
Ok(Out::OctetStream(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get state trie node by hash and return as raw binary.
|
/// Get state trie node by hash and return as raw binary.
|
||||||
fn contract_code(&self, hash: H256) -> Result<Out> {
|
fn contract_code(&self, hash: H256) -> Result<Out> {
|
||||||
let data = self.client.state_data(&hash).ok_or(Error::ContractNotFound)?;
|
let data = self.client().state_data(&hash).ok_or(Error::ContractNotFound)?;
|
||||||
|
|
||||||
Ok(Out::OctetStream(data))
|
Ok(Out::OctetStream(data))
|
||||||
}
|
}
|
||||||
@ -138,11 +114,12 @@ fn get_param<'a>(query: &'a str, name: &str) -> Option<&'a str> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::sync::Arc;
|
||||||
use super::*;
|
use super::*;
|
||||||
use ethcore::client::TestBlockChainClient;
|
use ethcore::client::TestBlockChainClient;
|
||||||
|
|
||||||
fn get_mocked_handler() -> IpfsHandler {
|
fn get_mocked_handler() -> IpfsHandler {
|
||||||
IpfsHandler::new(Arc::new(TestBlockChainClient::new()))
|
IpfsHandler::new(None, None, Arc::new(TestBlockChainClient::new()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -232,37 +209,37 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn route_block() {
|
fn route_block() {
|
||||||
let mut handler = get_mocked_handler();
|
let handler = get_mocked_handler();
|
||||||
|
|
||||||
let _ = handler.route("/api/v0/block/get", Some("arg=z43AaGF5tmkT9SEX6urrhwpEW5ZSaACY73Vw357ZXTsur2fR8BM"));
|
let out = handler.route("/api/v0/block/get", Some("arg=z43AaGF5tmkT9SEX6urrhwpEW5ZSaACY73Vw357ZXTsur2fR8BM"));
|
||||||
|
|
||||||
assert_eq!(handler.out, Out::NotFound("Block not found"));
|
assert_eq!(out, Out::NotFound("Block not found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn route_block_missing_query() {
|
fn route_block_missing_query() {
|
||||||
let mut handler = get_mocked_handler();
|
let handler = get_mocked_handler();
|
||||||
|
|
||||||
let _ = handler.route("/api/v0/block/get", None);
|
let out = handler.route("/api/v0/block/get", None);
|
||||||
|
|
||||||
assert_eq!(handler.out, Out::Bad("CID parsing failed"));
|
assert_eq!(out, Out::Bad("CID parsing failed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn route_block_invalid_query() {
|
fn route_block_invalid_query() {
|
||||||
let mut handler = get_mocked_handler();
|
let handler = get_mocked_handler();
|
||||||
|
|
||||||
let _ = handler.route("/api/v0/block/get", Some("arg=foobarz43AaGF5tmkT9SEX6urrhwpEW5ZSaACY73Vw357ZXTsur2fR8BM"));
|
let out = handler.route("/api/v0/block/get", Some("arg=foobarz43AaGF5tmkT9SEX6urrhwpEW5ZSaACY73Vw357ZXTsur2fR8BM"));
|
||||||
|
|
||||||
assert_eq!(handler.out, Out::Bad("CID parsing failed"));
|
assert_eq!(out, Out::Bad("CID parsing failed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn route_invalid_route() {
|
fn route_invalid_route() {
|
||||||
let mut handler = get_mocked_handler();
|
let handler = get_mocked_handler();
|
||||||
|
|
||||||
let _ = handler.route("/foo/bar/baz", Some("arg=z43AaGF5tmkT9SEX6urrhwpEW5ZSaACY73Vw357ZXTsur2fR8BM"));
|
let out = handler.route("/foo/bar/baz", Some("arg=z43AaGF5tmkT9SEX6urrhwpEW5ZSaACY73Vw357ZXTsur2fR8BM"));
|
||||||
|
|
||||||
assert_eq!(handler.out, Out::NotFound("Route not found"));
|
assert_eq!(out, Out::NotFound("Route not found"));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -76,6 +76,9 @@ path = "$HOME/.parity/secretstore"
|
|||||||
[ipfs]
|
[ipfs]
|
||||||
enable = false
|
enable = false
|
||||||
port = 5001
|
port = 5001
|
||||||
|
interface = "local"
|
||||||
|
cors = "null"
|
||||||
|
hosts = ["none"]
|
||||||
|
|
||||||
[mining]
|
[mining]
|
||||||
author = "0xdeadbeefcafe0000000000000000000000000001"
|
author = "0xdeadbeefcafe0000000000000000000000000001"
|
||||||
|
@ -96,7 +96,7 @@ usage! {
|
|||||||
|
|
||||||
// -- Account Options
|
// -- Account Options
|
||||||
flag_unlock: Option<String> = None,
|
flag_unlock: Option<String> = None,
|
||||||
or |c: &Config| otry!(c.account).unlock.clone().map(|vec| Some(vec.join(","))),
|
or |c: &Config| otry!(c.account).unlock.as_ref().map(|vec| Some(vec.join(","))),
|
||||||
flag_password: Vec<String> = Vec::new(),
|
flag_password: Vec<String> = Vec::new(),
|
||||||
or |c: &Config| otry!(c.account).password.clone(),
|
or |c: &Config| otry!(c.account).password.clone(),
|
||||||
flag_keys_iterations: u32 = 10240u32,
|
flag_keys_iterations: u32 = 10240u32,
|
||||||
@ -138,7 +138,7 @@ usage! {
|
|||||||
flag_network_id: Option<u64> = None,
|
flag_network_id: Option<u64> = None,
|
||||||
or |c: &Config| otry!(c.network).id.clone().map(Some),
|
or |c: &Config| otry!(c.network).id.clone().map(Some),
|
||||||
flag_bootnodes: Option<String> = None,
|
flag_bootnodes: Option<String> = None,
|
||||||
or |c: &Config| otry!(c.network).bootnodes.clone().map(|vec| Some(vec.join(","))),
|
or |c: &Config| otry!(c.network).bootnodes.as_ref().map(|vec| Some(vec.join(","))),
|
||||||
flag_no_discovery: bool = false,
|
flag_no_discovery: bool = false,
|
||||||
or |c: &Config| otry!(c.network).discovery.map(|d| !d).clone(),
|
or |c: &Config| otry!(c.network).discovery.map(|d| !d).clone(),
|
||||||
flag_node_key: Option<String> = None,
|
flag_node_key: Option<String> = None,
|
||||||
@ -160,9 +160,9 @@ usage! {
|
|||||||
flag_jsonrpc_cors: Option<String> = None,
|
flag_jsonrpc_cors: Option<String> = None,
|
||||||
or |c: &Config| otry!(c.rpc).cors.clone().map(Some),
|
or |c: &Config| otry!(c.rpc).cors.clone().map(Some),
|
||||||
flag_jsonrpc_apis: String = "web3,eth,net,parity,traces,rpc",
|
flag_jsonrpc_apis: String = "web3,eth,net,parity,traces,rpc",
|
||||||
or |c: &Config| otry!(c.rpc).apis.clone().map(|vec| vec.join(",")),
|
or |c: &Config| otry!(c.rpc).apis.as_ref().map(|vec| vec.join(",")),
|
||||||
flag_jsonrpc_hosts: String = "none",
|
flag_jsonrpc_hosts: String = "none",
|
||||||
or |c: &Config| otry!(c.rpc).hosts.clone().map(|vec| vec.join(",")),
|
or |c: &Config| otry!(c.rpc).hosts.as_ref().map(|vec| vec.join(",")),
|
||||||
|
|
||||||
// IPC
|
// IPC
|
||||||
flag_no_ipc: bool = false,
|
flag_no_ipc: bool = false,
|
||||||
@ -170,7 +170,7 @@ usage! {
|
|||||||
flag_ipc_path: String = "$BASE/jsonrpc.ipc",
|
flag_ipc_path: String = "$BASE/jsonrpc.ipc",
|
||||||
or |c: &Config| otry!(c.ipc).path.clone(),
|
or |c: &Config| otry!(c.ipc).path.clone(),
|
||||||
flag_ipc_apis: String = "web3,eth,net,parity,parity_accounts,traces,rpc",
|
flag_ipc_apis: String = "web3,eth,net,parity,parity_accounts,traces,rpc",
|
||||||
or |c: &Config| otry!(c.ipc).apis.clone().map(|vec| vec.join(",")),
|
or |c: &Config| otry!(c.ipc).apis.as_ref().map(|vec| vec.join(",")),
|
||||||
|
|
||||||
// DAPPS
|
// DAPPS
|
||||||
flag_no_dapps: bool = false,
|
flag_no_dapps: bool = false,
|
||||||
@ -180,7 +180,7 @@ usage! {
|
|||||||
flag_dapps_interface: String = "local",
|
flag_dapps_interface: String = "local",
|
||||||
or |c: &Config| otry!(c.dapps).interface.clone(),
|
or |c: &Config| otry!(c.dapps).interface.clone(),
|
||||||
flag_dapps_hosts: String = "none",
|
flag_dapps_hosts: String = "none",
|
||||||
or |c: &Config| otry!(c.dapps).hosts.clone().map(|vec| vec.join(",")),
|
or |c: &Config| otry!(c.dapps).hosts.as_ref().map(|vec| vec.join(",")),
|
||||||
flag_dapps_path: String = "$BASE/dapps",
|
flag_dapps_path: String = "$BASE/dapps",
|
||||||
or |c: &Config| otry!(c.dapps).path.clone(),
|
or |c: &Config| otry!(c.dapps).path.clone(),
|
||||||
flag_dapps_user: Option<String> = None,
|
flag_dapps_user: Option<String> = None,
|
||||||
@ -204,6 +204,12 @@ usage! {
|
|||||||
or |c: &Config| otry!(c.ipfs).enable.clone(),
|
or |c: &Config| otry!(c.ipfs).enable.clone(),
|
||||||
flag_ipfs_api_port: u16 = 5001u16,
|
flag_ipfs_api_port: u16 = 5001u16,
|
||||||
or |c: &Config| otry!(c.ipfs).port.clone(),
|
or |c: &Config| otry!(c.ipfs).port.clone(),
|
||||||
|
flag_ipfs_api_interface: String = "local",
|
||||||
|
or |c: &Config| otry!(c.ipfs).interface.clone(),
|
||||||
|
flag_ipfs_api_cors: Option<String> = None,
|
||||||
|
or |c: &Config| otry!(c.ipfs).cors.clone().map(Some),
|
||||||
|
flag_ipfs_api_hosts: String = "none",
|
||||||
|
or |c: &Config| otry!(c.ipfs).hosts.as_ref().map(|vec| vec.join(",")),
|
||||||
|
|
||||||
// -- Sealing/Mining Options
|
// -- Sealing/Mining Options
|
||||||
flag_author: Option<String> = None,
|
flag_author: Option<String> = None,
|
||||||
@ -249,7 +255,7 @@ usage! {
|
|||||||
flag_remove_solved: bool = false,
|
flag_remove_solved: bool = false,
|
||||||
or |c: &Config| otry!(c.mining).remove_solved.clone(),
|
or |c: &Config| otry!(c.mining).remove_solved.clone(),
|
||||||
flag_notify_work: Option<String> = None,
|
flag_notify_work: Option<String> = None,
|
||||||
or |c: &Config| otry!(c.mining).notify_work.clone().map(|vec| Some(vec.join(","))),
|
or |c: &Config| otry!(c.mining).notify_work.as_ref().map(|vec| Some(vec.join(","))),
|
||||||
flag_refuse_service_transactions: bool = false,
|
flag_refuse_service_transactions: bool = false,
|
||||||
or |c: &Config| otry!(c.mining).refuse_service_transactions.clone(),
|
or |c: &Config| otry!(c.mining).refuse_service_transactions.clone(),
|
||||||
|
|
||||||
@ -439,6 +445,9 @@ struct SecretStore {
|
|||||||
struct Ipfs {
|
struct Ipfs {
|
||||||
enable: Option<bool>,
|
enable: Option<bool>,
|
||||||
port: Option<u16>,
|
port: Option<u16>,
|
||||||
|
interface: Option<String>,
|
||||||
|
cors: Option<String>,
|
||||||
|
hosts: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, PartialEq, RustcDecodable)]
|
#[derive(Default, Debug, PartialEq, RustcDecodable)]
|
||||||
@ -678,6 +687,9 @@ mod tests {
|
|||||||
// IPFS
|
// IPFS
|
||||||
flag_ipfs_api: false,
|
flag_ipfs_api: false,
|
||||||
flag_ipfs_api_port: 5001u16,
|
flag_ipfs_api_port: 5001u16,
|
||||||
|
flag_ipfs_api_interface: "local".into(),
|
||||||
|
flag_ipfs_api_cors: Some("null".into()),
|
||||||
|
flag_ipfs_api_hosts: "none".into(),
|
||||||
|
|
||||||
// -- Sealing/Mining Options
|
// -- Sealing/Mining Options
|
||||||
flag_author: Some("0xdeadbeefcafe0000000000000000000000000001".into()),
|
flag_author: Some("0xdeadbeefcafe0000000000000000000000000001".into()),
|
||||||
@ -872,7 +884,10 @@ mod tests {
|
|||||||
}),
|
}),
|
||||||
ipfs: Some(Ipfs {
|
ipfs: Some(Ipfs {
|
||||||
enable: Some(false),
|
enable: Some(false),
|
||||||
port: Some(5001)
|
port: Some(5001),
|
||||||
|
interface: None,
|
||||||
|
cors: None,
|
||||||
|
hosts: None,
|
||||||
}),
|
}),
|
||||||
mining: Some(Mining {
|
mining: Some(Mining {
|
||||||
author: Some("0xdeadbeefcafe0000000000000000000000000001".into()),
|
author: Some("0xdeadbeefcafe0000000000000000000000000001".into()),
|
||||||
|
@ -178,6 +178,17 @@ API and Console Options:
|
|||||||
--ipfs-api Enable IPFS-compatible HTTP API. (default: {flag_ipfs_api})
|
--ipfs-api Enable IPFS-compatible HTTP API. (default: {flag_ipfs_api})
|
||||||
--ipfs-api-port PORT Configure on which port the IPFS HTTP API should listen.
|
--ipfs-api-port PORT Configure on which port the IPFS HTTP API should listen.
|
||||||
(default: {flag_ipfs_api_port})
|
(default: {flag_ipfs_api_port})
|
||||||
|
--ipfs-api-interface IP Specify the hostname portion of the IPFS API server,
|
||||||
|
IP should be an interface's IP address or local.
|
||||||
|
(default: {flag_ipfs_api_interface})
|
||||||
|
--ipfs-api-cors URL Specify CORS header for IPFS API responses.
|
||||||
|
(default: {flag_ipfs_api_cors:?})
|
||||||
|
--ipfs-api-hosts HOSTS List of allowed Host header values. This option will
|
||||||
|
validate the Host header sent by the browser, it
|
||||||
|
is additional security against some attack
|
||||||
|
vectors. Special options: "all", "none"
|
||||||
|
(default: {flag_ipfs_api_hosts}).
|
||||||
|
|
||||||
|
|
||||||
Secret Store Options:
|
Secret Store Options:
|
||||||
--no-secretstore Disable Secret Store functionality. (default: {flag_no_secretstore})
|
--no-secretstore Disable Secret Store functionality. (default: {flag_no_secretstore})
|
||||||
|
@ -558,6 +558,9 @@ impl Configuration {
|
|||||||
IpfsConfiguration {
|
IpfsConfiguration {
|
||||||
enabled: self.args.flag_ipfs_api,
|
enabled: self.args.flag_ipfs_api,
|
||||||
port: self.args.flag_ipfs_api_port,
|
port: self.args.flag_ipfs_api_port,
|
||||||
|
interface: self.ipfs_interface(),
|
||||||
|
cors: self.ipfs_cors(),
|
||||||
|
hosts: self.ipfs_hosts(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -693,29 +696,39 @@ impl Configuration {
|
|||||||
apis
|
apis
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cors(cors: Option<&String>) -> Option<Vec<String>> {
|
||||||
|
cors.map(|ref c| c.split(',').map(Into::into).collect())
|
||||||
|
}
|
||||||
|
|
||||||
fn rpc_cors(&self) -> Option<Vec<String>> {
|
fn rpc_cors(&self) -> Option<Vec<String>> {
|
||||||
let cors = self.args.flag_jsonrpc_cors.clone().or(self.args.flag_rpccorsdomain.clone());
|
let cors = self.args.flag_jsonrpc_cors.as_ref().or(self.args.flag_rpccorsdomain.as_ref());
|
||||||
cors.map(|c| c.split(',').map(|s| s.to_owned()).collect())
|
Self::cors(cors)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ipfs_cors(&self) -> Option<Vec<String>> {
|
||||||
|
Self::cors(self.args.flag_ipfs_api_cors.as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hosts(hosts: &str) -> Option<Vec<String>> {
|
||||||
|
match hosts {
|
||||||
|
"none" => return Some(Vec::new()),
|
||||||
|
"all" => return None,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
let hosts = hosts.split(',').map(Into::into).collect();
|
||||||
|
Some(hosts)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rpc_hosts(&self) -> Option<Vec<String>> {
|
fn rpc_hosts(&self) -> Option<Vec<String>> {
|
||||||
match self.args.flag_jsonrpc_hosts.as_ref() {
|
Self::hosts(&self.args.flag_jsonrpc_hosts)
|
||||||
"none" => return Some(Vec::new()),
|
|
||||||
"all" => return None,
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
let hosts = self.args.flag_jsonrpc_hosts.split(',').map(|h| h.into()).collect();
|
|
||||||
Some(hosts)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dapps_hosts(&self) -> Option<Vec<String>> {
|
fn dapps_hosts(&self) -> Option<Vec<String>> {
|
||||||
match self.args.flag_dapps_hosts.as_ref() {
|
Self::hosts(&self.args.flag_dapps_hosts)
|
||||||
"none" => return Some(Vec::new()),
|
|
||||||
"all" => return None,
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
let hosts = self.args.flag_dapps_hosts.split(',').map(|h| h.into()).collect();
|
|
||||||
Some(hosts)
|
fn ipfs_hosts(&self) -> Option<Vec<String>> {
|
||||||
|
Self::hosts(&self.args.flag_ipfs_api_hosts)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ipc_config(&self) -> Result<IpcConfiguration, String> {
|
fn ipc_config(&self) -> Result<IpcConfiguration, String> {
|
||||||
@ -850,14 +863,18 @@ impl Configuration {
|
|||||||
}.into()
|
}.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rpc_interface(&self) -> String {
|
fn interface(interface: &str) -> String {
|
||||||
match self.network_settings().rpc_interface.as_str() {
|
match interface {
|
||||||
"all" => "0.0.0.0",
|
"all" => "0.0.0.0",
|
||||||
"local" => "127.0.0.1",
|
"local" => "127.0.0.1",
|
||||||
x => x,
|
x => x,
|
||||||
}.into()
|
}.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn rpc_interface(&self) -> String {
|
||||||
|
Self::interface(&self.network_settings().rpc_interface)
|
||||||
|
}
|
||||||
|
|
||||||
fn dapps_interface(&self) -> String {
|
fn dapps_interface(&self) -> String {
|
||||||
match self.args.flag_dapps_interface.as_str() {
|
match self.args.flag_dapps_interface.as_str() {
|
||||||
"local" => "127.0.0.1",
|
"local" => "127.0.0.1",
|
||||||
@ -865,6 +882,10 @@ impl Configuration {
|
|||||||
}.into()
|
}.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ipfs_interface(&self) -> String {
|
||||||
|
Self::interface(&self.args.flag_ipfs_api_interface)
|
||||||
|
}
|
||||||
|
|
||||||
fn secretstore_interface(&self) -> String {
|
fn secretstore_interface(&self) -> String {
|
||||||
match self.args.flag_secretstore_interface.as_str() {
|
match self.args.flag_secretstore_interface.as_str() {
|
||||||
"local" => "127.0.0.1",
|
"local" => "127.0.0.1",
|
||||||
@ -873,11 +894,7 @@ impl Configuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn stratum_interface(&self) -> String {
|
fn stratum_interface(&self) -> String {
|
||||||
match self.args.flag_stratum_interface.as_str() {
|
Self::interface(&self.args.flag_stratum_interface)
|
||||||
"local" => "127.0.0.1",
|
|
||||||
"all" => "0.0.0.0",
|
|
||||||
x => x,
|
|
||||||
}.into()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dapps_enabled(&self) -> bool {
|
fn dapps_enabled(&self) -> bool {
|
||||||
@ -1273,6 +1290,38 @@ mod tests {
|
|||||||
assert_eq!(conf3.dapps_hosts(), Some(vec!["ethcore.io".into(), "something.io".into()]));
|
assert_eq!(conf3.dapps_hosts(), Some(vec!["ethcore.io".into(), "something.io".into()]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_parse_ipfs_hosts() {
|
||||||
|
// given
|
||||||
|
|
||||||
|
// when
|
||||||
|
let conf0 = parse(&["parity"]);
|
||||||
|
let conf1 = parse(&["parity", "--ipfs-api-hosts", "none"]);
|
||||||
|
let conf2 = parse(&["parity", "--ipfs-api-hosts", "all"]);
|
||||||
|
let conf3 = parse(&["parity", "--ipfs-api-hosts", "ethcore.io,something.io"]);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(conf0.ipfs_hosts(), Some(Vec::new()));
|
||||||
|
assert_eq!(conf1.ipfs_hosts(), Some(Vec::new()));
|
||||||
|
assert_eq!(conf2.ipfs_hosts(), None);
|
||||||
|
assert_eq!(conf3.ipfs_hosts(), Some(vec!["ethcore.io".into(), "something.io".into()]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_parse_ipfs_cors() {
|
||||||
|
// given
|
||||||
|
|
||||||
|
// when
|
||||||
|
let conf0 = parse(&["parity"]);
|
||||||
|
let conf1 = parse(&["parity", "--ipfs-api-cors", "*"]);
|
||||||
|
let conf2 = parse(&["parity", "--ipfs-api-cors", "http://ethcore.io,http://something.io"]);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(conf0.ipfs_cors(), None);
|
||||||
|
assert_eq!(conf1.ipfs_cors(), Some(vec!["*".into()]));
|
||||||
|
assert_eq!(conf2.ipfs_cors(), Some(vec!["http://ethcore.io".into(),"http://something.io".into()]));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_disable_signer_in_geth_compat() {
|
fn should_disable_signer_in_geth_compat() {
|
||||||
// given
|
// given
|
||||||
|
@ -1,9 +1,16 @@
|
|||||||
pub use parity_ipfs_api::start_server;
|
use std::sync::Arc;
|
||||||
|
use parity_ipfs_api;
|
||||||
|
use parity_ipfs_api::error::ServerError;
|
||||||
|
use ethcore::client::BlockChainClient;
|
||||||
|
use hyper::server::Listening;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct Configuration {
|
pub struct Configuration {
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
|
pub interface: String,
|
||||||
|
pub cors: Option<Vec<String>>,
|
||||||
|
pub hosts: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Configuration {
|
impl Default for Configuration {
|
||||||
@ -11,6 +18,23 @@ impl Default for Configuration {
|
|||||||
Configuration {
|
Configuration {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
port: 5001,
|
port: 5001,
|
||||||
|
interface: "127.0.0.1".into(),
|
||||||
|
cors: None,
|
||||||
|
hosts: Some(Vec::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn start_server(conf: Configuration, client: Arc<BlockChainClient>) -> Result<Option<Listening>, ServerError> {
|
||||||
|
if !conf.enabled {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
parity_ipfs_api::start_server(
|
||||||
|
conf.port,
|
||||||
|
conf.interface,
|
||||||
|
conf.cors,
|
||||||
|
conf.hosts,
|
||||||
|
client
|
||||||
|
).map(Some)
|
||||||
|
}
|
||||||
|
@ -471,10 +471,7 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
|
|||||||
let secretstore_key_server = secretstore::start(cmd.secretstore_conf.clone(), secretstore_deps);
|
let secretstore_key_server = secretstore::start(cmd.secretstore_conf.clone(), secretstore_deps);
|
||||||
|
|
||||||
// the ipfs server
|
// the ipfs server
|
||||||
let ipfs_server = match cmd.ipfs_conf.enabled {
|
let ipfs_server = ipfs::start_server(cmd.ipfs_conf.clone(), client.clone())?;
|
||||||
true => Some(ipfs::start_server(cmd.ipfs_conf.port, client.clone())?),
|
|
||||||
false => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// the informant
|
// the informant
|
||||||
let informant = Arc::new(Informant::new(
|
let informant = Arc::new(Informant::new(
|
||||||
|
Loading…
Reference in New Issue
Block a user