better error handling
This commit is contained in:
parent
7155c1cc54
commit
b7fa4677d8
4 changed files with 74 additions and 8 deletions
26
src/main.rs
26
src/main.rs
|
@ -1,37 +1,47 @@
|
||||||
#![feature(proc_macro_hygiene, decl_macro)]
|
#![feature(proc_macro_hygiene, decl_macro)]
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
#[macro_use] extern crate rocket;
|
#[macro_use] extern crate rocket;
|
||||||
use rocket::State;
|
use rocket::State;
|
||||||
|
use rocket::http::Status;
|
||||||
use rocket_contrib::json::Json;
|
use rocket_contrib::json::Json;
|
||||||
|
|
||||||
use trust_dns_client::client::{Client, SyncClient};
|
use trust_dns_client::client::{Client, SyncClient};
|
||||||
use trust_dns_client::tcp::TcpClientConnection;
|
use trust_dns_client::tcp::TcpClientConnection;
|
||||||
use trust_dns_client::op::DnsResponse;
|
use trust_dns_client::op::{DnsResponse, ResponseCode};
|
||||||
use trust_dns_client::rr::{DNSClass, Name, Record, RecordType};
|
use trust_dns_client::rr::{DNSClass, Name, Record, RecordType};
|
||||||
|
|
||||||
mod types;
|
mod models;
|
||||||
mod config;
|
mod config;
|
||||||
|
|
||||||
|
use models::errors::ErrorResponse;
|
||||||
|
|
||||||
|
|
||||||
#[get("/zones/<zone>/records")]
|
#[get("/zones/<zone>/records")]
|
||||||
fn zone_records(client: State<SyncClient<TcpClientConnection>>, zone: String) -> Json<Vec<types::dns::Record>> {
|
fn zone_records(client: State<SyncClient<TcpClientConnection>>, zone: String) -> Result<Json<Vec<models::dns::Record>>, ErrorResponse<()>> {
|
||||||
// TODO: Implement FromParam for Name
|
// TODO: Implement FromParam for Name
|
||||||
let name = Name::from_str(&zone).unwrap();
|
let name = Name::from_utf8(&zone).unwrap();
|
||||||
|
|
||||||
let response: DnsResponse = client.query(&name, DNSClass::IN, RecordType::AXFR).unwrap();
|
let response: DnsResponse = client.query(&name, DNSClass::IN, RecordType::AXFR).unwrap();
|
||||||
|
|
||||||
|
if response.response_code() != ResponseCode::NoError {
|
||||||
|
return ErrorResponse::new(
|
||||||
|
Status::NotFound,
|
||||||
|
format!("zone {} could not be found", name.to_utf8())
|
||||||
|
).err()
|
||||||
|
}
|
||||||
|
|
||||||
let answers: &[Record] = response.answers();
|
let answers: &[Record] = response.answers();
|
||||||
let mut records: Vec<_> = answers.to_vec().into_iter()
|
let mut records: Vec<_> = answers.to_vec().into_iter()
|
||||||
.map(|record| types::dns::Record::from(record))
|
.map(|record| models::dns::Record::from(record))
|
||||||
.filter(|record| match record.rdata {
|
.filter(|record| match record.rdata {
|
||||||
types::dns::RData::NULL { .. } | types::dns::RData::DNSSEC(_) => false,
|
models::dns::RData::NULL { .. } | models::dns::RData::DNSSEC(_) => false,
|
||||||
_ => true,
|
_ => true,
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
// AXFR response ends with SOA, we remove it so it is not doubled in the response.
|
// AXFR response ends with SOA, we remove it so it is not doubled in the response.
|
||||||
records.pop();
|
records.pop();
|
||||||
|
|
||||||
Json(records)
|
Ok(Json(records))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
55
src/models/errors.rs
Normal file
55
src/models/errors.rs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
use serde::Serialize;
|
||||||
|
use rocket::http::Status;
|
||||||
|
use rocket::request::Request;
|
||||||
|
use rocket::response::{self, Response, Responder};
|
||||||
|
use rocket_contrib::json::Json;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug)]
|
||||||
|
pub struct ErrorResponse<T> {
|
||||||
|
#[serde(with = "StatusDef")]
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub status: Status,
|
||||||
|
pub message: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub details: Option<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(remote = "Status")]
|
||||||
|
struct StatusDef {
|
||||||
|
code: u16,
|
||||||
|
#[serde(rename = "status")]
|
||||||
|
reason: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<T> ErrorResponse<T> {
|
||||||
|
pub fn new(status: Status, message: String) -> ErrorResponse<T> {
|
||||||
|
ErrorResponse {
|
||||||
|
status,
|
||||||
|
message,
|
||||||
|
details: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_details(self, details: T) -> ErrorResponse<T> {
|
||||||
|
ErrorResponse {
|
||||||
|
details: Some(details),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn err<R>(self) -> Result<R, ErrorResponse<T>> {
|
||||||
|
Err(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<'r, T: Serialize> Responder<'r> for ErrorResponse<T> {
|
||||||
|
fn respond_to(self, req: &Request) -> response::Result<'r> {
|
||||||
|
let status = self.status;
|
||||||
|
Response::build_from(Json(self).respond_to(req)?).status(status).ok()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
pub mod dns;
|
pub mod dns;
|
||||||
|
pub mod errors;
|
||||||
|
|
||||||
pub mod trust_dns_types {
|
pub mod trust_dns_types {
|
||||||
pub use trust_dns_client::rr::rdata::{
|
pub use trust_dns_client::rr::rdata::{
|
Loading…
Reference in a new issue