support decoding without mime type

This commit is contained in:
Sam Denty 2022-10-08 13:27:29 +01:00
parent 601d04020c
commit 16066479b4
No known key found for this signature in database
GPG key ID: 7B4EAF7B9E291B79
4 changed files with 41 additions and 39 deletions

View file

@ -8,12 +8,12 @@ use std::{
cmp::Ordering, cmp::Ordering,
error::Error, error::Error,
fmt::{self, Display}, fmt::{self, Display},
io::{self}, io,
}; };
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
#[serde(rename_all = "lowercase")] #[serde(rename_all = "lowercase")]
enum IconType { enum IconKind {
PNG, PNG,
JPEG, JPEG,
ICO, ICO,
@ -30,6 +30,30 @@ pub enum IconInfo {
} }
impl IconInfo { impl IconInfo {
async fn decode<R: AsyncRead + Unpin>(
reader: &mut R,
kind: Option<IconKind>,
) -> Result<IconInfo, Box<dyn Error>> {
let mut header = [0; 2];
reader.read_exact(&mut header).await?;
match (kind, header) {
(Some(IconKind::PNG), _) | (_, [0xC2, 0x89]) => {
let size = get_png_size(reader).await?;
Ok(IconInfo::PNG { size })
}
(Some(IconKind::ICO), _) | (_, [0x00, 0x00]) => {
let sizes = get_ico_sizes(reader).await?;
Ok(IconInfo::ICO { sizes })
}
(Some(IconKind::JPEG), _) | (_, [0xFF, 0xD8]) => {
let size = get_jpeg_size(reader).await?;
Ok(IconInfo::JPEG { size })
}
_ => Err("unknown icon type".into()),
}
}
pub async fn load( pub async fn load(
url: Url, url: Url,
headers: HeaderMap, headers: HeaderMap,
@ -85,7 +109,7 @@ impl IconInfo {
size: *sizes.largest(), size: *sizes.largest(),
}); });
} }
IconType::PNG Some(IconKind::PNG)
} }
(mime::IMAGE, mime::JPEG) => { (mime::IMAGE, mime::JPEG) => {
@ -94,7 +118,7 @@ impl IconInfo {
size: *sizes.largest(), size: *sizes.largest(),
}); });
} }
IconType::JPEG Some(IconKind::JPEG)
} }
(mime::IMAGE, "x-icon") | (mime::IMAGE, "vnd.microsoft.icon") => { (mime::IMAGE, "x-icon") | (mime::IMAGE, "vnd.microsoft.icon") => {
@ -102,28 +126,15 @@ impl IconInfo {
return Ok(IconInfo::ICO { sizes }); return Ok(IconInfo::ICO { sizes });
} }
IconType::ICO Some(IconKind::ICO)
} }
(mime::IMAGE, mime::SVG) => return Ok(IconInfo::SVG), (mime::IMAGE, mime::SVG) | (mime::TEXT, mime::PLAIN) => return Ok(IconInfo::SVG),
_ => return Err(format!("unsupported mime type {}", mime).into()), _ => None,
}; };
Ok(match kind { IconInfo::decode(&mut body, kind).await
IconType::PNG => {
let size = get_png_sizes(&mut body).await?;
IconInfo::PNG { size }
}
IconType::ICO => {
let sizes = get_ico_sizes(&mut body).await?;
IconInfo::ICO { sizes }
}
IconType::JPEG => {
let size = get_jpeg_size(&mut body).await?;
IconInfo::JPEG { size }
}
})
} }
pub fn size(&self) -> Option<&IconSize> { pub fn size(&self) -> Option<&IconSize> {

View file

@ -1,4 +1,4 @@
use super::{png::get_png_sizes, IconSize, IconSizes}; use super::{png::get_png_size, IconSize, IconSizes};
use byteorder::{LittleEndian, ReadBytesExt}; use byteorder::{LittleEndian, ReadBytesExt};
use futures::prelude::*; use futures::prelude::*;
use std::{ use std::{
@ -13,16 +13,14 @@ const INDEX_SIZE: u16 = 16;
pub async fn get_ico_sizes<R: AsyncRead + Unpin>( pub async fn get_ico_sizes<R: AsyncRead + Unpin>(
reader: &mut R, reader: &mut R,
) -> Result<IconSizes, Box<dyn Error>> { ) -> Result<IconSizes, Box<dyn Error>> {
let mut offset = 0; let mut offset = 4;
let mut header = [0; 6]; let mut header = [0; 4];
reader.read_exact(&mut header).await?; reader.read_exact(&mut header).await?;
offset += header.len();
let mut header = Cursor::new(header); let mut header = Cursor::new(header);
let header_type = header.read_u16::<LittleEndian>()?;
let icon_type = header.read_u16::<LittleEndian>()?; let icon_type = header.read_u16::<LittleEndian>()?;
if header_type != 0 || icon_type != ICO_TYPE { if icon_type != ICO_TYPE {
return Err("bad header".into()); return Err("bad header".into());
} }
@ -48,7 +46,7 @@ pub async fn get_ico_sizes<R: AsyncRead + Unpin>(
reader.read_exact(&mut data).await?; reader.read_exact(&mut data).await?;
offset += data.len(); offset += data.len();
let size = get_png_sizes(reader).await; let size = get_png_size(reader).await;
if let Ok(size) = size { if let Ok(size) = size {
sizes.push(size); sizes.push(size);
} }

View file

@ -7,13 +7,6 @@ use tokio_futures_byteorder::AsyncReadBytesExt;
pub async fn get_jpeg_size<R: AsyncRead + Unpin>( pub async fn get_jpeg_size<R: AsyncRead + Unpin>(
reader: &mut R, reader: &mut R,
) -> Result<IconSize, Box<dyn Error>> { ) -> Result<IconSize, Box<dyn Error>> {
let mut data = [0; 2];
reader.read_exact(&mut data).await?;
let data = &mut Cursor::new(data);
// first marker of the file MUST be 0xFFD8
assert_slice_eq!(data, 0, &[0xFF, 0xD8], "bad header");
let mut marker = [0; 2]; let mut marker = [0; 2];
let mut depth = 0i32; let mut depth = 0i32;

View file

@ -3,15 +3,15 @@ use byteorder::{BigEndian, ReadBytesExt};
use futures::prelude::*; use futures::prelude::*;
use std::{error::Error, io::Cursor}; use std::{error::Error, io::Cursor};
pub async fn get_png_sizes<R: AsyncRead + Unpin>( pub async fn get_png_size<R: AsyncRead + Unpin>(
reader: &mut R, reader: &mut R,
) -> Result<IconSize, Box<dyn Error>> { ) -> Result<IconSize, Box<dyn Error>> {
let mut header = [0; 24]; let mut header = [0; 22];
reader.read_exact(&mut header).await?; reader.read_exact(&mut header).await?;
let header = &mut Cursor::new(header); let header = &mut Cursor::new(header);
assert_slice_eq!(header, 0, b"\x89PNG\r\n\x1a\n", "bad header"); assert_slice_eq!(header, 0, b"PNG\r\n\x1a\n", "bad header");
assert_slice_eq!(header, 12, b"IHDR", "bad header"); assert_slice_eq!(header, 10, b"IHDR", "bad header");
let width = header.read_u32::<BigEndian>()?; let width = header.read_u32::<BigEndian>()?;
let height = header.read_u32::<BigEndian>()?; let height = header.read_u32::<BigEndian>()?;