Commit d6929db4 authored by TechnoElf's avatar TechnoElf
Browse files

Add http requests and json parsing

parent b772e496
#![feature(associated_type_bounds)]
use std::net::{TcpListener, TcpStream};
use std::io::{Read, Write};
use std::mem;
use std::collections::HashMap;
use std::fmt::Debug;
use std::time::Duration;
use rppal::hal::Delay;
use rppal::spi::{Spi, Bus, SlaveSelect, Mode};
use rppal::gpio::{Gpio, OutputPin};
use st7789::{ST7789, Orientation};
use display_interface_spi::SPIInterfaceNoCS;
use embedded_graphics::prelude::*;
use embedded_graphics::pixelcolor::Rgb565;
use embedded_graphics::draw_target::DrawTarget;
use embedded_graphics::mono_font::ascii::FONT_9X18_BOLD;
use embedded_graphics::mono_font::MonoTextStyle;
use embedded_graphics::text::{Text, Alignment};
use embedded_graphics::text::Text;
use embedded_graphics::primitives::{Circle, Rectangle, PrimitiveStyle};
use embedded_layout::prelude::*;
use embedded_layout::layout::linear::LinearLayout;
use json::JsonValue;
use json::object::Object;
use rppal::hal::Delay;
use rppal::spi::{Spi, Bus, SlaveSelect, Mode};
use rppal::gpio::{Gpio, OutputPin};
use st7789::{ST7789, Orientation};
const GATEWAY: &str = "192.168.254.1:80";
#[derive(Clone, Debug, Default)]
struct State {
content: Vec<(String, (u8, u8, u8))>
}
fn main() {
println!("{:?}", http_post(GATEWAY, "/", ""));
let spi_bus_0 = Spi::new(Bus::Spi0, SlaveSelect::Ss0, 5000000, Mode::Mode0).unwrap();
let spi_bus_1 = Spi::new(Bus::Spi0, SlaveSelect::Ss1, 5000000, Mode::Mode0).unwrap();
let dc_0 = Gpio::new().unwrap().get(12).unwrap().into_output();
......@@ -40,13 +59,12 @@ fn main() {
display_0.clear(Rgb565::new(255, 255, 255)).unwrap();
display_1.clear(Rgb565::new(255, 255, 255)).unwrap();
let style = MonoTextStyle::new(&FONT_9X18_BOLD, Rgb565::new(0, 0, 0));
let mut state = State::default();
state.content.resize(4, (String::new(), (0, 0, 0)));
draw_screen(&mut display_0, &state.content[0].0, &state.content[0].1, &state.content[1].0, &state.content[1].1);
draw_screen(&mut display_1, &state.content[2].0, &state.content[2].1, &state.content[3].0, &state.content[3].1);
Text::with_alignment("Release the Kraken!", Point::new(120, 120), style, Alignment::Center).draw(&mut display_0).unwrap();
Text::with_alignment("Release the Kraken!", Point::new(120, 120), style, Alignment::Center).draw(&mut display_1).unwrap();
//let mut gateway = TcpStream::connect(GATEWAY).unwrap();
//gateway.write("test".as_bytes()).unwrap();
eprintln!("Starting server...");
let listener = TcpListener::bind("0.0.0.0:80").unwrap();
for stream in listener.incoming() {
......@@ -61,37 +79,141 @@ fn main() {
let (uri, _ver) = rem.split_once(" ").unwrap();
let (path, _query) = uri.split_once("?").unwrap_or((uri, ""));
let resp = match (method, path) {
("POST", "/state") => {
display_0.clear(Rgb565::new(255, 255, 255)).unwrap();
display_1.clear(Rgb565::new(255, 255, 255)).unwrap();
LinearLayout::horizontal(Chain::new(
LinearLayout::vertical(
Chain::new(Rectangle::new(Point::zero(), Size::new(120, 1)).into_styled(PrimitiveStyle::with_fill(Rgb565::new(255, 255, 255)))).append(Circle::new(Point::zero(), 40).into_styled(PrimitiveStyle::with_fill(Rgb565::new(0, 255, 0)))).append(Text::new(&body, Point::zero(), style))
).with_alignment(horizontal::Center).arrange()).append(LinearLayout::vertical(
Chain::new(Rectangle::new(Point::zero(), Size::new(120, 1)).into_styled(PrimitiveStyle::with_fill(Rgb565::new(255, 255, 255)))).append(Circle::new(Point::zero(), 40).into_styled(PrimitiveStyle::with_fill(Rgb565::new(0, 255, 0)))).append(Text::new(&body, Point::zero(), style))
).with_alignment(horizontal::Center).arrange()
)).with_alignment(vertical::Center).arrange().align_to(&display_0.bounding_box(), horizontal::Center, vertical::Center).draw(&mut display_0).unwrap();
LinearLayout::horizontal(Chain::new(
LinearLayout::vertical(
Chain::new(Rectangle::new(Point::zero(), Size::new(120, 1)).into_styled(PrimitiveStyle::with_fill(Rgb565::new(255, 255, 255)))).append(Circle::new(Point::zero(), 40).into_styled(PrimitiveStyle::with_fill(Rgb565::new(0, 255, 0)))).append(Text::new(&body, Point::zero(), style))
).with_alignment(horizontal::Center).arrange()).append(LinearLayout::vertical(
Chain::new(Rectangle::new(Point::zero(), Size::new(120, 1)).into_styled(PrimitiveStyle::with_fill(Rgb565::new(255, 255, 255)))).append(Circle::new(Point::zero(), 40).into_styled(PrimitiveStyle::with_fill(Rgb565::new(0, 255, 0)))).append(Text::new(&body, Point::zero(), style))
).with_alignment(horizontal::Center).arrange()
)).with_alignment(vertical::Center).arrange().align_to(&display_0.bounding_box(), horizontal::Center, vertical::Center).draw(&mut display_1).unwrap();
"200 OK"
match process_request(method, path, body, &mut state) {
Ok(r) => {
stream.write(format!("HTTP/1.1 {}\r\n\r\n", r).as_bytes()).unwrap();
stream.flush().unwrap();
draw_screen(&mut display_0, &state.content[0].0, &state.content[0].1, &state.content[1].0, &state.content[1].1);
draw_screen(&mut display_1, &state.content[2].0, &state.content[2].1, &state.content[3].0, &state.content[3].1);
},
(m, u) => {
eprintln!("Invalid request: {} {}", m, u);
"404 NOT FOUND"
Err(r) => {
eprintln!("Invalid request: {} {}", method, uri);
stream.write(format!("HTTP/1.1 {}\r\n\r\n", r).as_bytes()).unwrap();
stream.flush().unwrap();
}
};
}
stream.write(format!("HTTP/1.1 {}\r\n\r\n", resp).as_bytes()).unwrap();
stream.flush().unwrap();
}
}
}
fn process_request(method: &str, path: &str, body: &str, state: &mut State) -> Result<&'static str, &'static str> {
match (method, path) {
("POST", "/state") => {
let request = json::parse(body).map_err(|_| "400 BAD REQUEST")?;
let requests = json_as_array(request).ok_or("400 BAD REQUEST")?;
for r in requests {
let mut r = json_as_object(r).ok_or("400 BAD REQUEST")?;
let id = r.remove("id").ok_or("400 BAD REQUEST")?.as_usize().ok_or("400 BAD REQUEST")?;
let text = r.remove("text").ok_or("400 BAD REQUEST")?.take_string().ok_or("400 BAD REQUEST")?;
let colour = json_as_colour(r.remove("colour").ok_or("400 BAD REQUEST")?).ok_or("400 BAD REQUEST")?;
if id < state.content.len() {
state.content[id].0 = text;
state.content[id].1 = colour;
}
}
Ok("200 OK")
},
_ => Err("404 NOT FOUND")
}
}
fn draw_screen<T: DrawTarget<Color = Rgb565, Error: Debug>>(disp: &mut T, text_0: &str, colour_0: &(u8, u8, u8), text_1: &str, colour_1: &(u8, u8, u8)) {
let style = MonoTextStyle::new(&FONT_9X18_BOLD, Rgb565::new(0, 0, 0));
disp.clear(Rgb565::new(255, 255, 255)).unwrap();
LinearLayout::horizontal(Chain::new(
LinearLayout::vertical(
Chain::new(Rectangle::new(Point::zero(), Size::new(120, 1)).into_styled(PrimitiveStyle::with_fill(Rgb565::new(255, 255, 255)))).append(Circle::new(Point::zero(), 40).into_styled(PrimitiveStyle::with_fill(Rgb565::new(colour_0.0, colour_0.1, colour_0.2)))).append(Text::new(text_0, Point::zero(), style))
).with_alignment(horizontal::Center).arrange()).append(LinearLayout::vertical(
Chain::new(Rectangle::new(Point::zero(), Size::new(120, 1)).into_styled(PrimitiveStyle::with_fill(Rgb565::new(255, 255, 255)))).append(Circle::new(Point::zero(), 40).into_styled(PrimitiveStyle::with_fill(Rgb565::new(colour_1.0, colour_1.1, colour_1.2)))).append(Text::new(text_1, Point::zero(), style))
).with_alignment(horizontal::Center).arrange()
)).with_alignment(vertical::Center).arrange().align_to(&disp.bounding_box(), horizontal::Center, vertical::Center).draw(disp).unwrap();
}
#[allow(dead_code)]
fn http_get(addr: &str, path: &str) -> Result<(String, u16), ()> {
let mut con = TcpStream::connect_timeout(&addr.parse().map_err(|_| ())?, Duration::from_secs(5)).map_err(|_| ())?;
con.set_read_timeout(Some(Duration::from_secs(5))).map_err(|_| ())?;
con.set_write_timeout(Some(Duration::from_secs(5))).map_err(|_| ())?;
let req = format!("HEAD {} HTTP/1.1\r\n\r\n", path);
con.write(req.as_bytes()).map_err(|_| ())?;
let mut buf = [0; 1024];
let header_size = con.read(&mut buf).map_err(|_| ())?;
let resp = String::from_utf8_lossy(&buf[0..header_size]);
let (head, _body) = resp.split_once("\r\n\r\n").ok_or(())?;
let (res, headers) = head.split_once("\r\n").ok_or(())?;
let (_ver, rem) = res.split_once(" ").ok_or(())?;
let (res_code, _res) = rem.split_once(" ").ok_or(())?;
let headers: HashMap<&str, &str> = headers.split("\r\n").filter_map(|h| h.split_once(": ")).collect();
let body_size: usize = headers.get("Content-Length").ok_or(())?.parse().map_err(|_| ())?;
let total_size = header_size + body_size;
let res_code = res_code.parse().map_err(|_| ())?;
let mut con = TcpStream::connect_timeout(&addr.parse().map_err(|_| ())?, Duration::from_secs(5)).map_err(|_| ())?;
con.set_read_timeout(Some(Duration::from_secs(5))).map_err(|_| ())?;
con.set_write_timeout(Some(Duration::from_secs(5))).map_err(|_| ())?;
let req = format!("GET {} HTTP/1.1\r\n\r\n", path);
con.write(req.as_bytes()).map_err(|_| ())?;
let mut buf: Vec<u8> = Vec::with_capacity(total_size);
buf.resize(total_size, 0);
con.read(&mut buf).map_err(|_| ())?;
let resp = String::from_utf8(buf).map_err(|_| ())?;
let (_head, body) = resp.split_once("\r\n\r\n").ok_or(())?;
Ok((body.to_string(), res_code))
}
fn http_post(addr: &str, path: &str, body: &str) -> Result<u16, ()> {
let mut con = TcpStream::connect_timeout(&addr.parse().map_err(|_| ())?, Duration::from_secs(5)).map_err(|_| ())?;
con.set_read_timeout(Some(Duration::from_secs(5))).map_err(|_| ())?;
con.set_write_timeout(Some(Duration::from_secs(5))).map_err(|_| ())?;
let req = format!("POST {} HTTP/1.1\r\n\r\n{}", path, body);
con.write(req.as_bytes()).map_err(|_| ())?;
let mut buf = [0; 1024];
let header_size = con.read(&mut buf).map_err(|_| ())?;
let resp = String::from_utf8_lossy(&buf[0..header_size]);
let (head, _body) = resp.split_once("\r\n\r\n").ok_or(())?;
let (res, headers) = head.split_once("\r\n").ok_or(())?;
let (_ver, rem) = res.split_once(" ").ok_or(())?;
let (res_code, _res) = rem.split_once(" ").ok_or(())?;
let headers: HashMap<&str, &str> = headers.split("\r\n").filter_map(|h| h.split_once(": ")).collect();
let body_size: usize = headers.get("Content-Length").ok_or(())?.parse().map_err(|_| ())?;
let _total_size = header_size + body_size;
let res_code = res_code.parse().map_err(|_| ())?;
Ok(res_code)
}
fn json_as_array(v: JsonValue) -> Option<Vec<JsonValue>> {
if let JsonValue::Array(v) = v { Some(v) } else { None }
}
fn json_as_object(v: JsonValue) -> Option<Object> {
if let JsonValue::Object(v) = v { Some(v) } else { None }
}
fn json_as_colour(v: JsonValue) -> Option<(u8, u8, u8)> {
let v = json_as_array(v)?;
if v.len() == 3 {
Some((v[0].as_u8()?, v[1].as_u8()?, v[2].as_u8()?))
} else {
None
}
}
Supports Markdown
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