Add the ability to return a super basic HTML page

This commit is contained in:
Frank Denis 2016-11-12 21:10:36 +01:00
parent ef3a57328e
commit f7d8e46752
4 changed files with 125 additions and 8 deletions

7
Cargo.lock generated
View File

@ -5,6 +5,7 @@ dependencies = [
"clap 2.18.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clippy 0.0.98 (registry+https://github.com/rust-lang/crates.io-index)",
"flate2 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"horrorshow 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)",
"iron 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -142,6 +143,11 @@ dependencies = [
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "horrorshow"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "hpack"
version = "0.2.0"
@ -742,6 +748,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum flate2 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "3eeb481e957304178d2e782f2da1257f1434dfecbae883bafb61ada2a9fea3bb"
"checksum gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)" = "553f11439bdefe755bf366b264820f1da70f3aaf3924e594b886beb9c831bcf5"
"checksum gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0912515a8ff24ba900422ecda800b52f4016a56251922d397c576bf92c690518"
"checksum horrorshow 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "234628f89d562b0ad97069e3f808cf109d0ed51b71a7825bfa6d32f317569543"
"checksum hpack 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d2da7d3a34cf6406d9d700111b8eafafe9a251de41ae71d8052748259343b58"
"checksum httparse 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6a8abece705b1d32c478f49447b3a575cd07f6e362ff12518f2ee2c9b9ced64e"
"checksum hyper 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)" = "d7da01615e9402761faab442396821b57ecb5adb12ac51958561411a82cfdf66"

View File

@ -6,6 +6,7 @@ authors = ["Frank Denis <github@pureftpd.org>"]
[dependencies]
clap = "*"
flate2 = "*"
horrorshow = "*"
hyper = "*"
iron = "*"
log = "*"

View File

@ -7,6 +7,8 @@ extern crate flate2;
extern crate iron;
#[macro_use]
extern crate router;
#[macro_use]
extern crate horrorshow;
extern crate hyper;
#[macro_use]
extern crate log;

View File

@ -1,6 +1,7 @@
use asns::*;
use horrorshow::prelude::*;
use iron::{BeforeMiddleware, typemap};
use iron::headers::{CacheControl, CacheDirective};
use iron::headers::{CacheControl, CacheDirective, Accept};
use iron::mime::*;
use iron::modifiers::Header;
use iron::prelude::*;
@ -34,6 +35,11 @@ impl BeforeMiddleware for ASNsMiddleware {
}
}
enum OutputType {
Json,
Html,
}
pub struct WebService;
impl WebService {
@ -47,13 +53,116 @@ impl WebService {
"See https://iptoasn.com")))
}
fn accept_type(req: &Request) -> OutputType {
let mut output_type = OutputType::Json;
if let Some(header_accept) = req.headers.get::<Accept>() {
for header in header_accept.iter() {
match header.item {
Mime(TopLevel::Text, SubLevel::Html, _) => {
output_type = OutputType::Html;
break;
}
Mime(_, SubLevel::Json, _) => {
output_type = OutputType::Json;
break;
}
_ => {}
}
}
}
output_type
}
fn output_json(_ip_str: &str,
map: serde_json::Map<&'static str, serde_json::value::Value>,
cache_header: Header<CacheControl>)
-> IronResult<Response> {
let json = serde_json::to_string(&map).unwrap();
let mime_json = Mime(TopLevel::Application,
SubLevel::Json,
vec![(Attr::Charset, Value::Utf8)]);
Ok(Response::with((status::Ok, mime_json, cache_header, json)))
}
fn output_html(ip_str: &str,
map: serde_json::Map<&'static str, serde_json::value::Value>,
cache_header: Header<CacheControl>)
-> IronResult<Response> {
let mime_html = Mime(TopLevel::Text,
SubLevel::Html,
vec![(Attr::Charset, Value::Utf8)]);
let html = html!{
head {
title { : "iptoasn lookup" }
meta(name="viewport", content="width=device-widthinitial-scale=1") { }
link(rel="stylesheet", href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.5/css/bootstrap.min.css", integrity="sha384-AysaV+vQoT3kOAXZkl02PThvDr8HYKPZhNT5h/CXfBThSRXQ6jW5DO2ekP5ViFdi", crossorigin="anonymous") { }
style {
: "body { margin: 1em 4em }"
}
}
body(class="container-fluid") {
header {
h1 {
: format_args!("Information for IP address: {}", ip_str)
}
}
table {
tr {
th { : "Announced" }
td { : format_args!("{}", map.get("announced")
.unwrap().as_bool().unwrap() ) }
}
@ if map.get("announced").unwrap().as_bool().unwrap() == true {
tr {
th { : "First IP" }
td { : format_args!("{}", map.get("first_ip")
.unwrap().as_str().unwrap() )}
}
tr {
th { : "Last IP" }
td { : format_args!("{}", map.get("last_ip")
.unwrap().as_str().unwrap() )}
}
tr {
th { : "AS Number" }
td { : format_args!("{}", map.get("as_number")
.unwrap().as_u64().unwrap() )}
}
tr {
th { : "AS Country code" }
td { : format_args!("{}", map.get("as_country_code")
.unwrap().as_str().unwrap() )}
}
tr {
th { : "AS Description" }
td { : format_args!("{}", map.get("as_description")
.unwrap().as_str().unwrap() )}
}
}
}
}
}
.into_string()
.unwrap();
let html = format!("<!doctype html>\n<html>\n{}</html>", html);
Ok(Response::with((status::Ok, mime_html, cache_header, html)))
}
fn output(output_type: OutputType,
ip_str: &str,
map: serde_json::Map<&'static str, serde_json::value::Value>,
cache_header: Header<CacheControl>)
-> IronResult<Response> {
match output_type {
OutputType::Json => Self::output_json(ip_str, map, cache_header),
_ => Self::output_html(ip_str, map, cache_header),
}
}
fn ip_lookup(req: &mut Request) -> IronResult<Response> {
let mime_text = Mime(TopLevel::Text,
SubLevel::Plain,
vec![(Attr::Charset, Value::Utf8)]);
let mime_json = Mime(TopLevel::Application,
SubLevel::Json,
vec![(Attr::Charset, Value::Utf8)]);
let cache_header = Header(CacheControl(vec![CacheDirective::Public,
CacheDirective::MaxAge(TTL)]));
let ip_str = match req.extensions.get::<Router>().unwrap().find("ip") {
@ -80,8 +189,7 @@ impl WebService {
None => {
let mut map = serde_json::Map::new();
map.insert("announced", serde_json::value::Value::Bool(false));
let json = serde_json::to_string(&map).unwrap();
return Ok(Response::with((status::Ok, mime_json, cache_header, json)));
return Self::output(Self::accept_type(&req), ip_str, map, cache_header);
}
Some(found) => found,
};
@ -97,8 +205,7 @@ impl WebService {
serde_json::value::Value::String(found.country.clone()));
map.insert("as_description",
serde_json::value::Value::String(found.description.clone()));
let json = serde_json::to_string(&map).unwrap();
Ok(Response::with((status::Ok, mime_json, cache_header, json)))
Self::output(Self::accept_type(&req), ip_str, map, cache_header)
}
pub fn start(asns_arc: Arc<RwLock<Arc<ASNs>>>, listen_addr: &str) {