mirror of
https://github.com/bytedream/docker4ssh.git
synced 2026-02-04 11:56:25 +01:00
Initial commit
This commit is contained in:
157
container/src/shared/api/api.rs
Normal file
157
container/src/shared/api/api.rs
Normal file
@@ -0,0 +1,157 @@
|
||||
use std::collections::HashMap;
|
||||
use std::io::{Read, Write};
|
||||
use std::net::TcpStream;
|
||||
use log::Level::Error;
|
||||
use serde::Deserialize;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, failure::Error>;
|
||||
|
||||
pub struct API {
|
||||
route: String,
|
||||
host: String,
|
||||
}
|
||||
|
||||
impl API {
|
||||
pub const fn new(route: String, host: String) -> Self {
|
||||
API {
|
||||
route,
|
||||
host,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_connection(&mut self) -> Result<TcpStream> {
|
||||
match TcpStream::connect(&self.route) {
|
||||
Ok(stream) => Ok(stream),
|
||||
Err(e) => Err(failure::format_err!("Failed to connect to {}: {}", self.route, e.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request(&mut self, request: &Request) -> Result<APIResult> {
|
||||
let mut connection = self.new_connection()?;
|
||||
|
||||
connection.write_all(request.as_string().as_bytes())?;
|
||||
let mut buf: String = String::new();
|
||||
connection.read_to_string(&mut buf).map_err(|e| failure::err_msg(e.to_string()))?;
|
||||
Ok(APIResult::new(request, buf))
|
||||
}
|
||||
|
||||
pub fn request_with_err(&mut self, request: &Request) -> Result<APIResult> {
|
||||
let result = self.request(request)?;
|
||||
if result.result_code >= 400 {
|
||||
let err: APIError = result.body()?;
|
||||
Err(failure::err_msg(format!("Error {}: {}", result.result_code, err.message)))
|
||||
} else {
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct APIError {
|
||||
message: String
|
||||
}
|
||||
|
||||
pub struct APIResult {
|
||||
// TODO: Store the whole request instead of only the path
|
||||
request_path: String,
|
||||
|
||||
result_code: i32,
|
||||
result_body: String
|
||||
}
|
||||
|
||||
impl APIResult {
|
||||
fn new(request: &Request, raw_response: String) -> Self {
|
||||
APIResult {
|
||||
request_path: request.path.clone(),
|
||||
|
||||
// TODO: Parse http body better
|
||||
result_code: raw_response[9..12].parse().unwrap(),
|
||||
result_body: raw_response.split_once("\r\n\r\n").unwrap().1.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path(self) -> String {
|
||||
self.request_path
|
||||
}
|
||||
|
||||
pub fn code(&self) -> i32 {
|
||||
return self.result_code
|
||||
}
|
||||
|
||||
pub fn has_body(&self) -> bool {
|
||||
self.result_body.len() > 0
|
||||
}
|
||||
|
||||
pub fn body<'a, T: Deserialize<'a>>(&'a self) -> Result<T> {
|
||||
let result: T = serde_json::from_str(&self.result_body).map_err(|e| {
|
||||
// checks if the error has a body and if so, return it
|
||||
if self.has_body() {
|
||||
let error: APIError = serde_json::from_str(&self.result_body).unwrap_or_else(|ee| {
|
||||
APIError{message: format!("could not deserialize response: {}", e.to_string())}
|
||||
});
|
||||
failure::format_err!("Failed to call '{}': {}", self.request_path, error.message)
|
||||
} else {
|
||||
failure::format_err!("Failed to call '{}': {}", self.request_path, e.to_string())
|
||||
}
|
||||
})?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Method {
|
||||
GET,
|
||||
POST
|
||||
}
|
||||
|
||||
pub struct Request {
|
||||
method: Method,
|
||||
path: String,
|
||||
headers: HashMap<String, String>,
|
||||
body: String,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
pub fn new(path: String) -> Self {
|
||||
Request{
|
||||
method: Method::GET,
|
||||
path,
|
||||
headers: Default::default(),
|
||||
body: "".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_method(&mut self, method: Method) -> &Self {
|
||||
self.method = method;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_path(&mut self, path: String) -> &Self {
|
||||
self.path = path;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_header(&mut self, field: &str, value: String) -> &Self {
|
||||
self.headers.insert(String::from(field), value);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_body(&mut self, body: String) -> &Self {
|
||||
self.body = body;
|
||||
self.headers.insert("Content-Length".to_string(), self.body.len().to_string());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn as_string(&self) -> String {
|
||||
let method;
|
||||
match self.method {
|
||||
Method::GET => method = "GET",
|
||||
Method::POST => method = "POST"
|
||||
}
|
||||
|
||||
let headers_as_string = self.headers.iter().map(|f| format!("{}: {}", f.0, f.1)).collect::<String>();
|
||||
|
||||
return format!("{} {} HTTP/1.0\r\n\
|
||||
{}\r\n\r\n\
|
||||
{}\r\n", method, self.path, headers_as_string, self.body)
|
||||
}
|
||||
}
|
||||
2
container/src/shared/api/mod.rs
Normal file
2
container/src/shared/api/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod request;
|
||||
pub mod api;
|
||||
220
container/src/shared/api/request.rs
Normal file
220
container/src/shared/api/request.rs
Normal file
@@ -0,0 +1,220 @@
|
||||
use std::fmt::{Display, Formatter};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::de::Unexpected::Str;
|
||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
|
||||
use crate::shared::api::api::{API, Method, Request, Result};
|
||||
use crate::shared::api::api::Method::POST;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct PingResponse {
|
||||
pub received: u128
|
||||
}
|
||||
|
||||
pub struct PingRequest {
|
||||
request: Request
|
||||
}
|
||||
|
||||
impl PingRequest {
|
||||
pub fn new() -> Self {
|
||||
PingRequest {
|
||||
request: Request::new(String::from("/ping"))
|
||||
}
|
||||
}
|
||||
pub fn request(&self, api: &mut API) -> Result<PingResponse> {
|
||||
let result: PingResponse = api.request_with_err(&self.request)?.body()?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ErrorRequest {
|
||||
request: Request
|
||||
}
|
||||
|
||||
impl ErrorRequest {
|
||||
pub fn new() -> Self {
|
||||
ErrorRequest {
|
||||
request: Request::new(String::from("/error"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request(&self, api: &mut API) -> Result<()> {
|
||||
api.request_with_err(&self.request)?.body()?;
|
||||
// should never call Ok
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct InfoResponse {
|
||||
pub container_id: String
|
||||
}
|
||||
|
||||
pub struct InfoRequest {
|
||||
request: Request
|
||||
}
|
||||
|
||||
impl InfoRequest {
|
||||
pub fn new() -> Self {
|
||||
InfoRequest{
|
||||
request: Request::new(String::from("/info"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request(&self, api: &mut API) -> Result<InfoResponse> {
|
||||
let result: InfoResponse = api.request_with_err(&self.request)?.body()?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize_repr, Deserialize_repr)]
|
||||
#[repr(u8)]
|
||||
pub enum ConfigRunLevel {
|
||||
User = 1,
|
||||
Container = 2,
|
||||
Forever = 3
|
||||
}
|
||||
|
||||
impl Display for ConfigRunLevel {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize_repr, Deserialize_repr)]
|
||||
#[repr(u8)]
|
||||
pub enum ConfigNetworkMode {
|
||||
Off = 1,
|
||||
Full = 2,
|
||||
Host = 3,
|
||||
Docker = 4,
|
||||
None = 5
|
||||
}
|
||||
|
||||
impl Display for ConfigNetworkMode {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ConfigGetResponse {
|
||||
pub network_mode: ConfigNetworkMode,
|
||||
pub configurable: bool,
|
||||
pub run_level: ConfigRunLevel,
|
||||
pub startup_information: bool,
|
||||
pub exit_after: String,
|
||||
pub keep_on_exit: bool
|
||||
}
|
||||
|
||||
pub struct ConfigGetRequest {
|
||||
request: Request
|
||||
}
|
||||
|
||||
impl ConfigGetRequest {
|
||||
pub fn new() -> ConfigGetRequest {
|
||||
ConfigGetRequest{
|
||||
request: Request::new(String::from("/config"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request(&self, api: &mut API) -> Result<ConfigGetResponse> {
|
||||
let result: ConfigGetResponse = api.request_with_err(&self.request)?.body()?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct ConfigPostBody {
|
||||
pub network_mode: Option<ConfigNetworkMode>,
|
||||
pub configurable: Option<bool>,
|
||||
pub run_level: Option<ConfigRunLevel>,
|
||||
pub startup_information: Option<bool>,
|
||||
pub exit_after: Option<String>,
|
||||
pub keep_on_exit: Option<bool>
|
||||
}
|
||||
|
||||
pub struct ConfigPostRequest {
|
||||
request: Request,
|
||||
pub body: ConfigPostBody
|
||||
}
|
||||
|
||||
impl ConfigPostRequest {
|
||||
pub fn new() -> ConfigPostRequest {
|
||||
let mut request = Request::new(String::from("/config"));
|
||||
request.set_method(Method::POST);
|
||||
|
||||
ConfigPostRequest {
|
||||
request,
|
||||
body: ConfigPostBody{
|
||||
network_mode: None,
|
||||
configurable: None,
|
||||
run_level: None,
|
||||
startup_information: None,
|
||||
exit_after: None,
|
||||
keep_on_exit: None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request(&mut self, api: &mut API) -> Result<()> {
|
||||
self.request.set_body(serde_json::to_string(&self.body)?);
|
||||
api.request_with_err(&self.request)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct AuthGetResponse {
|
||||
pub user: String,
|
||||
pub has_password: bool
|
||||
}
|
||||
|
||||
pub struct AuthGetRequest {
|
||||
request: Request
|
||||
}
|
||||
|
||||
impl AuthGetRequest {
|
||||
pub fn new() -> AuthGetRequest {
|
||||
AuthGetRequest{
|
||||
request: Request::new(String::from("/auth"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request(&self, api: &mut API) -> Result<AuthGetResponse> {
|
||||
let result: AuthGetResponse = api.request_with_err(&self.request)?.body()?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct AuthPostBody {
|
||||
pub user: Option<String>,
|
||||
pub password: Option<String>
|
||||
}
|
||||
|
||||
pub struct AuthPostRequest {
|
||||
request: Request,
|
||||
pub body: AuthPostBody
|
||||
}
|
||||
|
||||
impl AuthPostRequest {
|
||||
pub fn new() -> AuthPostRequest {
|
||||
let mut request = Request::new(String::from("/auth"));
|
||||
request.set_method(POST);
|
||||
|
||||
AuthPostRequest {
|
||||
request,
|
||||
body: AuthPostBody{
|
||||
user: None,
|
||||
password: None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request(&mut self, api: &mut API) -> Result<()> {
|
||||
self.request.set_body(serde_json::to_string(&self.body)?);
|
||||
api.request_with_err(&self.request)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
19
container/src/shared/logging/logger.rs
Normal file
19
container/src/shared/logging/logger.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
use log::{info, Metadata, Record};
|
||||
|
||||
pub struct Logger;
|
||||
|
||||
impl log::Log for Logger {
|
||||
fn enabled(&self, metadata: &Metadata) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn log(&self, record: &Record) {
|
||||
if self.enabled(record.metadata()) {
|
||||
println!("{}", record.args().to_string())
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&self) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
11
container/src/shared/logging/mod.rs
Normal file
11
container/src/shared/logging/mod.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
use log::{LevelFilter, SetLoggerError};
|
||||
|
||||
pub mod logger;
|
||||
|
||||
pub use logger::Logger;
|
||||
|
||||
static LOGGER: Logger = Logger;
|
||||
|
||||
pub fn init_logger(level: LevelFilter) -> Result<(), SetLoggerError> {
|
||||
log::set_logger(&Logger).map(|()| log::set_max_level(level))
|
||||
}
|
||||
2
container/src/shared/mod.rs
Normal file
2
container/src/shared/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod api;
|
||||
pub mod logging;
|
||||
Reference in New Issue
Block a user