2018-03-05 22:33:44 +03:00
|
|
|
// Copyright 2018 The Grin Developers
|
2017-03-08 04:00:34 +03:00
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
//! RESTful API server to easily expose services as RESTful JSON/HTTP endpoints.
|
|
|
|
//! Fairly constrained on what the service API must look like by design.
|
|
|
|
//!
|
|
|
|
//! To use it, just have your service(s) implement the ApiEndpoint trait and
|
|
|
|
//! register them on a ApiServer.
|
|
|
|
|
2018-08-01 12:44:07 +03:00
|
|
|
use hyper::rt::Future;
|
|
|
|
use hyper::service::service_fn;
|
|
|
|
use hyper::{Body, Request, Server};
|
|
|
|
use router::ResponseFuture;
|
2018-04-17 00:18:28 +03:00
|
|
|
use std::fmt::{self, Display};
|
2018-08-01 12:44:07 +03:00
|
|
|
use std::net::SocketAddr;
|
|
|
|
use tokio::runtime::current_thread::Runtime;
|
2017-03-08 04:00:34 +03:00
|
|
|
|
2018-04-17 00:18:28 +03:00
|
|
|
use failure::{Backtrace, Context, Fail};
|
2017-03-08 04:00:34 +03:00
|
|
|
|
|
|
|
/// Errors that can be returned by an ApiEndpoint implementation.
|
|
|
|
#[derive(Debug)]
|
2018-04-16 12:00:32 +03:00
|
|
|
pub struct Error {
|
|
|
|
inner: Context<ErrorKind>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Eq, PartialEq, Debug, Fail)]
|
|
|
|
pub enum ErrorKind {
|
|
|
|
#[fail(display = "Internal error: {}", _0)]
|
2017-03-08 04:00:34 +03:00
|
|
|
Internal(String),
|
2018-04-16 12:00:32 +03:00
|
|
|
#[fail(display = "Bad arguments: {}", _0)]
|
2017-03-08 04:00:34 +03:00
|
|
|
Argument(String),
|
2018-04-16 12:00:32 +03:00
|
|
|
#[fail(display = "Not found.")]
|
2017-06-13 02:41:27 +03:00
|
|
|
NotFound,
|
2018-08-01 12:44:07 +03:00
|
|
|
#[fail(display = "Request error: {}", _0)]
|
|
|
|
RequestError(String),
|
|
|
|
#[fail(display = "ResponseError error: {}", _0)]
|
|
|
|
ResponseError(String),
|
2017-03-08 04:00:34 +03:00
|
|
|
}
|
|
|
|
|
2018-04-16 12:00:32 +03:00
|
|
|
impl Fail for Error {
|
|
|
|
fn cause(&self) -> Option<&Fail> {
|
|
|
|
self.inner.cause()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn backtrace(&self) -> Option<&Backtrace> {
|
|
|
|
self.inner.backtrace()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-26 03:21:56 +03:00
|
|
|
impl Display for Error {
|
2018-04-16 12:00:32 +03:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
Display::fmt(&self.inner, f)
|
2017-03-08 04:00:34 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-16 12:00:32 +03:00
|
|
|
impl Error {
|
|
|
|
pub fn kind(&self) -> &ErrorKind {
|
|
|
|
self.inner.get_context()
|
2017-03-08 04:00:34 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-16 12:00:32 +03:00
|
|
|
impl From<ErrorKind> for Error {
|
|
|
|
fn from(kind: ErrorKind) -> Error {
|
|
|
|
Error {
|
|
|
|
inner: Context::new(kind),
|
2017-06-13 02:41:27 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-16 12:00:32 +03:00
|
|
|
impl From<Context<ErrorKind>> for Error {
|
|
|
|
fn from(inner: Context<ErrorKind>) -> Error {
|
|
|
|
Error { inner: inner }
|
2017-03-08 04:00:34 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// HTTP server allowing the registration of ApiEndpoint implementations.
|
2018-08-01 12:44:07 +03:00
|
|
|
pub struct ApiServer {}
|
2017-03-08 04:00:34 +03:00
|
|
|
|
|
|
|
impl ApiServer {
|
|
|
|
/// Creates a new ApiServer that will serve ApiEndpoint implementations
|
|
|
|
/// under the root URL.
|
2018-08-01 12:44:07 +03:00
|
|
|
pub fn new() -> ApiServer {
|
|
|
|
ApiServer {}
|
2017-03-08 04:00:34 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Starts the ApiServer at the provided address.
|
2018-08-01 12:44:07 +03:00
|
|
|
pub fn start<F>(&mut self, addr: SocketAddr, f: &'static F) -> Result<(), String>
|
|
|
|
where
|
|
|
|
F: Fn(Request<Body>) -> ResponseFuture + Send + Sync + 'static,
|
|
|
|
{
|
|
|
|
let server = Server::bind(&addr)
|
|
|
|
.serve(move || service_fn(f))
|
|
|
|
.map_err(|e| eprintln!("server error: {}", e));
|
|
|
|
|
|
|
|
let mut rt = Runtime::new().unwrap();
|
|
|
|
if rt.block_on(server).is_err() {
|
|
|
|
return Err("tokio block_on error".to_owned());
|
|
|
|
}
|
|
|
|
Ok(())
|
2017-06-16 19:47:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Stops the API server
|
2017-09-29 21:44:25 +03:00
|
|
|
pub fn stop(&mut self) {
|
2018-08-01 12:44:07 +03:00
|
|
|
// TODO implement proper stop, the following method doesn't
|
|
|
|
// work for current_thread runtime.
|
|
|
|
// if let Some(rt) = self.rt.take() {
|
|
|
|
// rt.shutdown_now().wait().unwrap();
|
|
|
|
// }
|
2017-03-08 04:00:34 +03:00
|
|
|
}
|
|
|
|
}
|