Commit b7fa4677 authored by Gaël Berthaud-Müller's avatar Gaël Berthaud-Müller
Browse files

better error handling

parent 7155c1cc
#![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() {
......
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()
}
}
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::{
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment